From e7301bcd63c0cbfc4d55f21d0d7c33c7557d6444 Mon Sep 17 00:00:00 2001 From: ckonstanski Date: Sun, 19 Jul 2026 22:05:02 -0600 Subject: starting resume logic --- lisp/resume.asd | 12 +- lisp/service/about-us-service.lisp | 4 +- lisp/service/menu-service.lisp | 4 +- lisp/service/resume-service.lisp | 122 ++++++++++ lisp/sql/contactinfo.lisp | 30 +++ lisp/sql/education.lisp | 33 +++ lisp/sql/job.lisp | 27 +++ lisp/sql/language.lisp | 30 +++ lisp/sql/resume-pkg.lisp | 19 ++ lisp/sql/resume.lisp | 18 ++ lisp/sql/skill.lisp | 15 ++ lisp/sql/states.lisp | 18 ++ lisp/sql/visastatus.lisp | 15 ++ .../resume/clojurescript/resume/src/core.cljs | 268 ++++++++++++++++----- lisp/webapps/resume/site.lisp | 93 ++++--- lisp/webapps/resume/static/css/stylesheet.css | 45 ++-- sql/functions/resume.insert_resume.sql | 30 +++ sql/tables/resume.contactinfo.sql | 2 +- sql/tables/resume.resume.sql | 3 +- 19 files changed, 651 insertions(+), 137 deletions(-) create mode 100644 lisp/service/resume-service.lisp create mode 100644 lisp/sql/contactinfo.lisp create mode 100644 lisp/sql/education.lisp create mode 100644 lisp/sql/job.lisp create mode 100644 lisp/sql/language.lisp create mode 100644 lisp/sql/resume-pkg.lisp create mode 100644 lisp/sql/resume.lisp create mode 100644 lisp/sql/skill.lisp create mode 100644 lisp/sql/states.lisp create mode 100644 lisp/sql/visastatus.lisp create mode 100644 sql/functions/resume.insert_resume.sql diff --git a/lisp/resume.asd b/lisp/resume.asd index a63bc46..d746d6f 100644 --- a/lisp/resume.asd +++ b/lisp/resume.asd @@ -44,9 +44,18 @@ (:file "registration" :depends-on ("generics")) (:file "about-us" :depends-on ("generics")) (:file "contact-us" :depends-on ("generics")) + (:file "contactinfo" :depends-on ("generics")) + (:file "education" :depends-on ("generics")) + (:file "job" :depends-on ("generics")) + (:file "language" :depends-on ("generics")) + (:file "resume" :depends-on ("generics")) + (:file "skill" :depends-on ("generics")) + (:file "states" :depends-on ("generics")) + (:file "visastatus" :depends-on ("generics")) (:file "auth-pkg" :depends-on ("user-session-pkg" "user" "role-group" "user-role" "registration")) (:file "general-pkg" :depends-on ("about-us")) - (:file "contact-pkg" :depends-on ("contact-us")))) + (:file "contact-pkg" :depends-on ("contact-us")) + (:file "resume-pkg" :depends-on ("auth-pkg" "contactinfo" "education" "job" "language" "resume" "skill" "states" "visastatus")))) (:module service :depends-on (sql) :components ((:file "generics") @@ -62,6 +71,7 @@ (:file "password-service" :depends-on ("generic-form" "auth-service")) (:file "about-us-service" :depends-on ("generic-form" "auth-service")) (:file "contact-us-service" :depends-on ("generic-form" "auth-service")) + (:file "resume-service" :depends-on ("generic-form" "auth-service")) (:file "users-service" :depends-on ("generic-form" "auth-service")) (:file "messages-service" :depends-on ("generic-form" "auth-service")))) (:module webapps diff --git a/lisp/service/about-us-service.lisp b/lisp/service/about-us-service.lisp index c479efe..71b5b70 100644 --- a/lisp/service/about-us-service.lisp +++ b/lisp/service/about-us-service.lisp @@ -9,8 +9,10 @@ :accessor category)) (:documentation "")) -(defun about-us-json (category) +(defun about-us-json (category &optional message errormsg) (with-noauth (instance about-us-service) + (when (not (org-ckons-core::null-or-empty-p message)) (setf (message instance) message)) + (when (not (org-ckons-core::null-or-empty-p errormsg)) (setf (errormsg instance) errormsg)) (setf (category instance) category))) (defclass about-us/view-service (about-us-service) diff --git a/lisp/service/menu-service.lisp b/lisp/service/menu-service.lisp index e7af0c3..6e97992 100644 --- a/lisp/service/menu-service.lisp +++ b/lisp/service/menu-service.lisp @@ -4,9 +4,7 @@ (in-package :resume) (defparameter *menu-config* '((:id "a_menu_home" :label "Home" :handler "/home" :permissions "_Public") - (:id "a_menu_lessons" :label "About the Studio" :handler "/lessons" :permissions "_Public") - (:id "a_menu_gigs" :label "For Hire" :handler "/gigs" :permissions "_Public") - (:id "a_menu_programming" :label "Software Consulting" :handler "/programming" :permissions "_Public") + (:id "a_menu_resumes" :label "Resumes" :handler "/resumes" :permissions "resume-view") (:id "a_menu_contact_us" :label "Contact Me" :handler "/contact-us" :permissions "_Public") (:id "a_menu_messages" :label "Messages" :handler "/messages" :permissions "messages-view") (:id "a_menu_users" :label "Users" :handler "/users" :permissions "users-view"))) diff --git a/lisp/service/resume-service.lisp b/lisp/service/resume-service.lisp new file mode 100644 index 0000000..2da011a --- /dev/null +++ b/lisp/service/resume-service.lisp @@ -0,0 +1,122 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass resumes-service (auth-service) + ((title :initarg :title + :initform nil + :accessor title)) + (:documentation "")) + +(defun resumes-json () + (with-auth (instance resumes-service "resume-view") + (setf (title instance) "Resumes"))) + +(defclass resumes/view-service (resumes-service resume) + ((resumes :initarg :resumes + :initform nil + :accessor resumes) + (location-p :initform nil)) + (:documentation "")) + +(defun resumes-view-json () + (with-auth (instance resumes/view-service "resume-view") + (with-resume-database + (let ((resume-pkg (make-instance 'resume-pkg)) + (user (get-user))) + (setf (resumes instance) (get-all-resumes-for-user resume-pkg user)))) + (sanitize-rest-json instance) + (loop for resume in (resumes instance) + do (sanitize-json resume)))) + +(defclass resumes/add-service (resumes/view-service) + ((form :initarg :form + :initform nil + :accessor form)) + (:documentation "")) + +(defun resumes-add-json () + (with-auth (instance resumes/add-service "resume-modify") + (setf (title instance) "Resume - Add") + (setf (form instance) (make-form "resumes-add-form" + nil + t + `((:name "name" :label "Name" :field-type "text" :required "required") + (:label "Add Resume" :field-type "button" :onclick "on_resumes_add_submit_clicked()")))))) + +(defun resumes-add-submit-json (name) + (declare (special name)) + (with-auth (instance resumes/add-service "resume-modify") + (with-resume-database + (handler-case + (let* ((resume-pkg (make-instance 'resume-pkg)) + (user (get-user)) + (resume (make-instance 'resume :user_id (id user) :name name))) + (insert-resume resume-pkg resume) + (setf (session-value :message) (format nil "Resume \"~a\" created successfully." (name resume)))) + (error (e) + (setf (session-value :errormsg) (format nil "Error creating resume \"~a\". ~a" name e))))))) + +(defclass resumes/modify-service (resumes/add-service) + () + (:documentation "")) + +(defun resumes-modify-json (id) + (with-auth (instance resumes/modify-service "resume-modify") + (setf (title instance) "Resume - Modify") + (with-resume-database + (let* ((resume-pkg (make-instance 'resume-pkg)) + (me (get-resume)) + (resume (get-resume-by-id resume-pkg id)) + (role-groups (get-all-role-groups resume-pkg me)) + (active-role-groups (get-active-role-groups resume-pkg resume))) + (if resume + (setf (form instance) (make-form "resumes-modify-form" + nil + t + `((:name "id" :field-type "hidden" :value ,(id resume) :required "required") + (:name "name" :label "Name" :field-type "text" :value ,(name resume) :required "required") + (:label "Modify Resume" :field-type "button" :onclick "on_resumes_modify_submit_clicked()")))) + (setf (session-value :errormsg) "Error: could not modify resume. Not found.")))))) + +(defun resumes-modify-submit-json (id role_groups resumename first_name last_name email phone) + (declare (special role_groups resumename first_name last_name email phone)) + (with-auth (instance resumes/modify-service "resume-modify") + (with-resume-database + (let* ((resume-pkg (make-instance 'resume-pkg)) + (resume (get-active-resume-by-id resume-pkg id))) + (if resume + (progn + (loop for param in (remove-if (lambda (x) + (intersection `(,x) '(id role_groups))) + (sb-introspect:function-lambda-list #'resumes-modify-submit-json)) + do (setf (slot-value resume param) (symbol-value param))) + (update-resume resume-pkg resume) + (delete-role-groups resume-pkg resume) + (loop for role-group in (union '("profile-admin") + (cl-ppcre:split "\\|" role_groups) + :test 'string=) + do (insert-resume-role-group resume-pkg (make-instance 'resume-role + :resume_id (id resume) + :role_group_name role-group))) + (setf (session-value :message) "Resume saved successfully.")) + (setf (session-value :errormsg) "An error occured.")))))) + +(defclass resumes/delete-service (resumes/add-service) + () + (:documentation "")) + +(defun resumes-delete-json (id) + (with-auth (instance resumes/delete-service "resume-modify") + (with-resume-database + (let* ((resume-pkg (make-instance 'resume-pkg)) + (resume (get-resume-by-id resume-pkg id))) + (if resume + (if (= (id resume) (id (get-resume))) + (setf (session-value :errormsg) "Error: you may not delete yourself.") + (progn + (deactivate-resume resume-pkg resume) + (setf (session-value :errormsg) nil) + (setf (session-value :message) "Resume deleted successfully."))) + (setf (session-value :errormsg) "Error: could not delete resume: not found.")))))) diff --git a/lisp/sql/contactinfo.lisp b/lisp/sql/contactinfo.lisp new file mode 100644 index 0000000..83c271d --- /dev/null +++ b/lisp/sql/contactinfo.lisp @@ -0,0 +1,30 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass contactinfo (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (address :initarg :address + :initform nil + :accessor address) + (city :initarg :city + :initform nil + :accessor city) + (state_id :initarg :state_id + :initform nil + :accessor state_id) + (phone :initarg :phone + :initform nil + :accessor phone) + (email :initarg :email + :initform nil + :accessor email) + (visastatus_id :initarg :visastatus_id + :initform nil + :accessor visastatus_id) + (*table :initform "resume.contactinfo") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.contactinfo record.")) diff --git a/lisp/sql/education.lisp b/lisp/sql/education.lisp new file mode 100644 index 0000000..96b5382 --- /dev/null +++ b/lisp/sql/education.lisp @@ -0,0 +1,33 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass education (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (school :initarg :school + :initform nil + :accessor school) + (start_date :initarg :start_date + :initform nil + :accessor start_date) + (end_date :initarg :end_date + :initform nil + :accessor end_date) + (major :initarg :major + :initform nil + :accessor major) + (minor :initarg :minor + :initform nil + :accessor minor) + (degree :initarg :degree + :initform nil + :accessor degree) + (progress :initarg :progress + :initform nil + :accessor progress) + (*table :initform "resume.education") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.education record.")) diff --git a/lisp/sql/job.lisp b/lisp/sql/job.lisp new file mode 100644 index 0000000..96f9796 --- /dev/null +++ b/lisp/sql/job.lisp @@ -0,0 +1,27 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass job (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (company :initarg :company + :initform nil + :accessor company) + (location :initarg :location + :initform nil + :accessor location) + (start_date :initarg :start_date + :initform nil + :accessor start_date) + (end_date :initarg :end_date + :initform nil + :accessor end_date) + (overview :initarg :overview + :initform nil + :accessor overview) + (*table :initform "resume.job") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.job record.")) diff --git a/lisp/sql/language.lisp b/lisp/sql/language.lisp new file mode 100644 index 0000000..ca384b1 --- /dev/null +++ b/lisp/sql/language.lisp @@ -0,0 +1,30 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass language (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (name :initarg :name + :initform nil + :accessor name) + (understanding_listening :initarg :understanding_listening + :initform nil + :accessor understanding_listening) + (understanding_reading :initarg :understanding_reading + :initform nil + :accessor understanding_reading) + (speaking_interaction :initarg :speaking_interaction + :initform nil + :accessor speaking_interaction) + (speaking_production :initarg :speaking_production + :initform nil + :accessor speaking_production) + (writing :initarg :writing + :initform nil + :accessor writing) + (*table :initform "resume.language") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.language record.")) diff --git a/lisp/sql/resume-pkg.lisp b/lisp/sql/resume-pkg.lisp new file mode 100644 index 0000000..3c8c289 --- /dev/null +++ b/lisp/sql/resume-pkg.lisp @@ -0,0 +1,19 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass resume-pkg (record-pkg) + () + (:documentation "")) + +(defmethod get-all-resumes-for-user ((resume-pkg resume-pkg) (user user)) + (let ((resume (make-instance 'resume :user_id (id user)))) + (get-records resume-pkg resume "name asc"))) + +(defmethod insert-resume ((resume-pkg resume-pkg) (resume resume)) + (setf (*table resume) (format nil + "resume.insert_resume(~a, '~a')" + (user_id resume) + (name resume))) + (caar (call-pg-function resume-pkg resume))) diff --git a/lisp/sql/resume.lisp b/lisp/sql/resume.lisp new file mode 100644 index 0000000..8949c20 --- /dev/null +++ b/lisp/sql/resume.lisp @@ -0,0 +1,18 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass resume (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (user_id :initarg :user_id + :initform nil + :accessor user_id) + (name :initarg :name + :initform nil + :accessor name) + (*table :initform "resume.resume") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.resume record.")) diff --git a/lisp/sql/skill.lisp b/lisp/sql/skill.lisp new file mode 100644 index 0000000..3b43d29 --- /dev/null +++ b/lisp/sql/skill.lisp @@ -0,0 +1,15 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass skill (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (skill :initarg :skill + :initform nil + :accessor skill) + (*table :initform "resume.skill") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.skill record.")) diff --git a/lisp/sql/states.lisp b/lisp/sql/states.lisp new file mode 100644 index 0000000..7968c16 --- /dev/null +++ b/lisp/sql/states.lisp @@ -0,0 +1,18 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass states (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (state :initarg :state + :initform nil + :accessor state) + (abbr :initarg :abbr + :initform nil + :accessor abbr) + (*table :initform "resume.states") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.states record.")) diff --git a/lisp/sql/visastatus.lisp b/lisp/sql/visastatus.lisp new file mode 100644 index 0000000..3f6293c --- /dev/null +++ b/lisp/sql/visastatus.lisp @@ -0,0 +1,15 @@ +;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*- +(declaim (optimize (speed 0) (safety 3) (debug 3))) + +(in-package :resume) + +(defclass visastatus (postgres-record) + ((id :initarg :id + :initform nil + :accessor id) + (visastatus :initarg :visastatus + :initform nil + :accessor visastatus) + (*table :initform "resume.visastatus") + (*where-expression :initform "id = ~a")) + (:documentation "Holds the data for a resume.visastatus record.")) diff --git a/lisp/webapps/resume/clojurescript/resume/src/core.cljs b/lisp/webapps/resume/clojurescript/resume/src/core.cljs index 7010a88..a84c806 100644 --- a/lisp/webapps/resume/clojurescript/resume/src/core.cljs +++ b/lisp/webapps/resume/clojurescript/resume/src/core.cljs @@ -29,8 +29,6 @@ (declare handler-menu) (declare handler-menu-user) (declare render-menu) -(declare comp-home) -(declare handler-home) (declare render-home) (declare template-login) (declare handler-login) @@ -128,6 +126,29 @@ (declare on-users-delete-clicked) (declare handler-users-delete) (declare render-users-delete) +(declare template-resumes) +(declare handler-resumes) +(declare render-resumes) +(declare template-resumes-view) +(declare handler-resumes-view) +(declare render-resumes-view) +(declare on-resumes-add-clicked) +(declare template-resumes-add) +(declare handler-resumes-add) +(declare render-resumes-add) +(declare on-resumes-add-submit-clicked) +(declare handler-resumes-add-submit) +(declare render-resumes-add-submit) +(declare on-resumes-modify-clicked) +(declare template-resumes-modify) +(declare handler-resumes-modify) +(declare render-resumes-modify) +(declare on-resumes-modify-submit-clicked) +(declare handler-resumes-modify-submit) +(declare render-resumes-modify-submit) +(declare on-resumes-delete-clicked) +(declare handler-resumes-delete) +(declare render-resumes-delete) (declare on-menu-clicked) (declare handler-location) (declare goto-location) @@ -137,14 +158,14 @@ (enable-console-print!) -(defonce jquery (js* "$")) -(defonce sql-formatter (time-format/formatter "yyyy-MM-dd HH:mm:ss")) -(defonce pretty-formatter (time-format/formatters :rfc822)) -(defonce location-state (r/atom "/home")) -(defonce about-us-category-state (r/atom nil)) -(defonce menu-main-state (r/atom [])) -(defonce menu-user-state (r/atom [])) -(defonce menu-user-label (r/atom nil)) +(def jquery (js* "$")) +(def sql-formatter (time-format/formatter "yyyy-MM-dd HH:mm:ss")) +(def pretty-formatter (time-format/formatters :rfc822)) +(def location-state (r/atom "/home")) +(def about-us-category-state (r/atom "home")) +(def menu-main-state (r/atom [])) +(def menu-user-state (r/atom [])) +(def menu-user-label (r/atom "Guest User")) ;; helper functions @@ -182,7 +203,7 @@ [:tr [:td {:class "banner-menu"} [comp-menu-user]] - [:td {:class "banner-title"} "Bogen-" [:i "Herr"]] + [:td {:class "banner-title"} "Resume Builder"] [:td {:class "banner-menu"} " "]]]]] [comp-menu-main] [ck-notifications/comp-errormsg] @@ -242,7 +263,7 @@ (defn comp-menu-main [] [:div {:class "well"} [:ul {:class "nav nav-pills"} - (for [menuitem @menu-main-state] + (for [menuitem (doall @menu-main-state)] [:li {:key (str "li_" (get menuitem "id")) :class "nav-item"} [:a {:class (cond (= (str/upper-case (get menuitem "handler")) @@ -264,7 +285,7 @@ :aria-expanded "false"} @menu-user-label] [:div {:class "dropdown-menu" :aria-labelledby "button-menu-user"} - (for [menuitem @menu-user-state] + (for [menuitem (doall @menu-user-state)] [:a {:key (get menuitem "id") :class "dropdown-item" :on-click #(on-menu-clicked (get menuitem "handler"))} @@ -283,33 +304,6 @@ (GET "/menu" {:handler handler-menu}) (GET "/menu/user" {:handler handler-menu-user})) -;; home - -(hiccups/defhtml template-home [jsonobj] - [:div - [:h2 {:style "text-align: center"} "Welcome To Carlos Konstanski's Music Studio"] - [:h3 {:style "text-align: center"} - [:i "a.k.a. der Bogenherr" [:br] "a.k.a. Dr. Divertimento"]] - [:h2 {:style "text-align: center"} "Study the Violin, Viola and Viola d'Amore With Me!"] - [:div {:style "text-align: center"} - [:img {:style "width: 100%" - :src "/static/images/instrument-cabinet.jpg"}]]]) - -(defn handler-home [response] - (let [jsonobj (js->clj (js/JSON.parse response))] - (notifications jsonobj) - (dommy/set-html! (dommy/sel1 :#body) (template-home jsonobj)))) - -(defn render-home - ([] - (GET "/home" {:handler handler-home})) - ([message errormsg] - (POST "/home" - {:format :raw - :params {:message message - :errormsg errormsg} - :handler handler-home}))) - ;; login (hiccups/defhtml template-login [jsonobj] @@ -506,13 +500,11 @@ :pwd2 (dommy/value (dommy/sel1 :#pwd2))} :handler handler-password-submit})) -;; about-us (lessons, gigs, programming) +;; about-us (home) (hiccups/defhtml template-about-us [jsonobj] [:h1 {:style "text-align: center"} - (cond (= @about-us-category-state "lessons") "About the Studio" - (= @about-us-category-state "gigs") "Hire Me to Play" - (= @about-us-category-state "programming") "Hire Me to Write Software")] + (cond (= @about-us-category-state "home") "Home")] [:div {:id "content"}] [:div {:id "modify" :class "modal fade" @@ -543,14 +535,15 @@ (dommy/set-html! (dommy/sel1 :#body) (template-about-us jsonobj)) (render-about-us-view))) -(defn render-lessons [] - (GET "/lessons" {:handler handler-about-us})) - -(defn render-gigs [] - (GET "/gigs" {:handler handler-about-us})) - -(defn render-programming [] - (GET "/programming" {:handler handler-about-us})) +(defn render-home + ([] + (GET "/home" {:handler handler-about-us})) + ([message errormsg] + (POST "/home" + {:format :raw + :params {:message message + :errormsg errormsg} + :handler handler-about-us}))) ;; about-us-view @@ -583,9 +576,7 @@ (let [jsonobj (js->clj (js/JSON.parse response))] (auth-notifications jsonobj) (dommy/set-html! (dommy/sel1 :#modify-title) - (str (cond (= @about-us-category-state "lessons") "About the Studio" - (= @about-us-category-state "gigs") "For Hire" - (= @about-us-category-state "programming") "Software Consulting") + (str (cond (= @about-us-category-state "home") "Home") " - Modify")) (dommy/set-html! (dommy/sel1 :#modify-body) (template-about-us-modify jsonobj)) (.modal (jquery "#modify")))) @@ -1036,6 +1027,167 @@ {:format :raw :handler handler-users-delete})) +;; resumes + +(hiccups/defhtml template-resumes [jsonobj] + [:h3 {:style "text-align: center"} (get jsonobj "title")] + [:div {:id "content"}] + [:div {:id "modify" + :class "modal fade" + :role "dialog" + :tabindex "-1"} + [:div {:class "modal-dialog modal-lg"} + [:div {:class "modal-content"} + [:div {:class "modal-header"} + [:h5 [:span {:id "modify-title"}]] + [:button {:type "button" + :class "close" + :data-dismiss "modal"} + "×"]] + [:div {:id "modify-body" + :class "modal-body" + :style "height: 450px;"}] + [:div {:class "modal-footer"} + [:button {:type "submit" + :class "btn btn-danger btn-default" + :data-dismiss "modal"} + [:span {:class "glyphicon glyphicon-remove"}] + "Cancel"]]]]]) + +(defn handler-resumes [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (notifications jsonobj) + (dommy/set-html! (dommy/sel1 :#body) (template-resumes jsonobj)) + (render-resumes-view))) + +(defn render-resumes [] + (GET "/resumes" {:handler handler-resumes})) + +;; resumes-view + +(hiccups/defhtml template-resumes-view [jsonobj] + [:div {:style "text-align: right"} + [:img {:src "/static/images/add.png" + :style "cursor:pointer; cursor:hand" + :onclick (str (namespace ::x) ".on_resumes_add_clicked()")}]] + [:table {:class "table table-striped table-hover table-sm"} + [:thead + [:tr + [:th {:scope "col"} "Name"] + [:th {:scope "col"} "Del"]]] + [:tbody + (for [resume (get jsonobj "resumes")] + (let [onclick (str (namespace ::x) ".on_resumes_modify_clicked(" (get resume "id") ")") + toggle-active (str (namespace ::x) ".on_resumes_toggle_active_clicked(" (get resume "id") ")")] + [:tr + [:td {:onclick onclick} (get resume "name")] + [:td {:width "50"} + [:img {:src "/static/images/delete.png" + :onclick (str (namespace ::x) ".on_resumes_delete_clicked(" (get resume "id") ", '" (get resume "name") "')")}]]]))]]) + +(defn handler-resumes-view [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (dommy/set-html! (dommy/sel1 :#content) (template-resumes-view jsonobj)))) + +(defn render-resumes-view [] + (GET "/resumes/view" {:handler handler-resumes-view})) + +;; resumes-add + +(defn on-resumes-add-clicked [] + (render-resumes-add)) + +(hiccups/defhtml template-resumes-add [jsonobj] + (ck-form/template-generic-form (get jsonobj "form") (namespace ::x))) + +(defn handler-resumes-add [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (auth-notifications jsonobj) + (dommy/set-html! (dommy/sel1 :#modify-title) (get jsonobj "title")) + (dommy/set-html! (dommy/sel1 :#modify-body) (template-resumes-add jsonobj)) + (.modal (jquery "#modify")))) + +(defn render-resumes-add [] + (POST "/resumes/add" {:handler handler-resumes-add})) + +;; resumes-add-submit + +(defn on-resumes-add-submit-clicked [] + (when (-> (jquery "#resumes-add-form") + (.get "0") + (.checkValidity)) + (.modal (jquery "#modify") "hide") + (render-resumes-add-submit))) + +(defn handler-resumes-add-submit [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (auth-notifications jsonobj) + (on-menu-clicked "/resumes"))) + +(defn render-resumes-add-submit [] + (POST "/resumes/add/submit" + {:format :raw + :params {:name (dommy/value (dommy/sel1 :#name))} + :handler handler-resumes-add-submit})) + +;; resumes-modify + +(defn on-resumes-modify-clicked [id] + (render-resumes-modify id)) + +(hiccups/defhtml template-resumes-modify [jsonobj] + (ck-form/template-generic-form (get jsonobj "form") (namespace ::x))) + +(defn handler-resumes-modify [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (auth-notifications jsonobj) + (dommy/set-html! (dommy/sel1 :#modify-title) (get jsonobj "title")) + (dommy/set-html! (dommy/sel1 :#modify-body) (template-resumes-modify jsonobj)) + (.modal (jquery "#modify")))) + +(defn render-resumes-modify [id] + (POST "/resumes/modify" + {:format :raw + :params {:id id} + :handler handler-resumes-modify})) + +;; resumes-modify-submit + +(defn on-resumes-modify-submit-clicked [] + (when (-> (jquery "#resumes-modify-form") + (.get "0") + (.checkValidity)) + (.modal (jquery "#modify") "hide") + (render-resumes-modify-submit))) + +(defn handler-resumes-modify-submit [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (auth-notifications jsonobj) + (on-menu-clicked "/resumes"))) + +(defn render-resumes-modify-submit [] + (POST "/resumes/modify/submit" + {:format :raw + :params {:id (dommy/value (dommy/sel1 :#id)) + :name (dommy/value (dommy/sel1 :#name))} + :handler handler-resumes-modify-submit})) + +;; resumes-delete + +(defn on-resumes-delete-clicked [id name] + (when (js/confirm (str "Are you sure you want to delete " name "? This action cannot be undone.")) + (render-resumes-delete id))) + +(defn handler-resumes-delete [response] + (let [jsonobj (js->clj (js/JSON.parse response))] + (auth-notifications jsonobj) + (on-menu-clicked "/resumes"))) + +(defn render-resumes-delete [id] + (DELETE (str "/resumes/delete/" id) + {:format :raw + :handler handler-resumes-delete})) + ;; location (defn on-menu-clicked [handler] @@ -1046,12 +1198,10 @@ (= handler "/logout") (render-logout) (= handler "/profile") (render-profile) (= handler "/password") (render-password) - (= handler "/lessons") (render-lessons) - (= handler "/gigs") (render-gigs) - (= handler "/programming") (render-programming) (= handler "/contact-us") (render-contact-us) (= handler "/messages") (render-messages) - (= handler "/users") (render-users))) + (= handler "/users") (render-users) + (= handler "/resumes") (render-resumes))) (defn handler-location [response] (let [jsonobj (js->clj (js/JSON.parse response))] diff --git a/lisp/webapps/resume/site.lisp b/lisp/webapps/resume/site.lisp index 3e31ac1..a7e5916 100644 --- a/lisp/webapps/resume/site.lisp +++ b/lisp/webapps/resume/site.lisp @@ -30,10 +30,19 @@ `(location-json location)) (defmacro .home-get () - `(home-json)) + `(about-us-json "home")) (defmacro .home-post () - `(home-json message errormsg)) + `(about-us-json "home" message errormsg)) + +(defmacro .home-view () + `(about-us-view-json "home")) + +(defmacro .home-modify () + `(about-us-modify-json "home")) + +(defmacro .home-modify-submit () + `(about-us-modify-submit-json "home" content)) (defmacro .menu () `(menu-json)) @@ -71,42 +80,6 @@ (defmacro .password-submit () `(password-submit-json id pwd pwd2)) -(defmacro .lessons () - `(about-us-json "lessons")) - -(defmacro .lessons-view () - `(about-us-view-json "lessons")) - -(defmacro .lessons-modify () - `(about-us-modify-json "lessons")) - -(defmacro .lessons-modify-submit () - `(about-us-modify-submit-json "lessons" content)) - -(defmacro .gigs () - `(about-us-json "gigs")) - -(defmacro .gigs-view () - `(about-us-view-json "gigs")) - -(defmacro .gigs-modify () - `(about-us-modify-json "gigs")) - -(defmacro .gigs-modify-submit () - `(about-us-modify-submit-json "gigs" content)) - -(defmacro .programming () - `(about-us-json "programming")) - -(defmacro .programming-view () - `(about-us-view-json "programming")) - -(defmacro .programming-modify () - `(about-us-modify-json "programming")) - -(defmacro .programming-modify-submit () - `(about-us-modify-submit-json "programming" content)) - (defmacro .contact-us () `(contact-us-json)) @@ -158,10 +131,34 @@ (defmacro .users-delete () `(users-delete-json id)) +(defmacro .resumes () + `(resumes-json)) + +(defmacro .resumes-view () + `(resumes-view-json)) + +(defmacro .resumes-add () + `(resumes-add-json)) + +(defmacro .resumes-add-submit () + `(resumes-add-submit-json name)) + +(defmacro .resumes-modify () + `(resumes-modify-json id)) + +(defmacro .resumes-modify-submit () + `(resumes-modify-submit-json id name)) + +(defmacro .resumes-delete () + `(users-delete-json id)) + (define-endpoint ("/" :method :get) () .base) (define-endpoint ("/location" :method :post) (&post (location :parameter-type 'string)) .location) (define-endpoint ("/home" :method :get) () .home-get) (define-endpoint ("/home" :method :post) (&post (message :parameter-type 'string) (errormsg :parameter-type 'string)) .home-post) +(define-endpoint ("/home/view" :method :get) () .home-view) +(define-endpoint ("/home/modify" :method :post) () .home-modify) +(define-endpoint ("/home/modify/submit" :method :post) (&post (content :parameter-type 'string)) .home-modify-submit) (define-endpoint ("/menu" :method :get) () .menu) (define-endpoint ("/menu/user" :method :get) () .menu-user) (define-endpoint ("/login" :method :get) () .login) @@ -174,18 +171,6 @@ (define-endpoint ("/profile/modify/submit" :method :post) (&post (id :parameter-type 'integer) (username :parameter-type 'string) (first_name :parameter-type 'string) (last_name :parameter-type 'string) (email :parameter-type 'string) (phone :parameter-type 'string)) .profile-modify-submit) (define-endpoint ("/password" :method :get) () .password) (define-endpoint ("/password/submit" :method :post) (&post (id :parameter-type 'integer) (pwd :parameter-type 'string) (pwd2 :parameter-type 'string)) .password-submit) -(define-endpoint ("/lessons" :method :get) () .lessons) -(define-endpoint ("/lessons/view" :method :get) () .lessons-view) -(define-endpoint ("/lessons/modify" :method :post) () .lessons-modify) -(define-endpoint ("/lessons/modify/submit" :method :post) (&post (content :parameter-type 'string)) .lessons-modify-submit) -(define-endpoint ("/gigs" :method :get) () .gigs) -(define-endpoint ("/gigs/view" :method :get) () .gigs-view) -(define-endpoint ("/gigs/modify" :method :post) () .gigs-modify) -(define-endpoint ("/gigs/modify/submit" :method :post) (&post (content :parameter-type 'string)) .gigs-modify-submit) -(define-endpoint ("/programming" :method :get) () .programming) -(define-endpoint ("/programming/view" :method :get) () .programming-view) -(define-endpoint ("/programming/modify" :method :post) () .programming-modify) -(define-endpoint ("/programming/modify/submit" :method :post) (&post (content :parameter-type 'string)) .programming-modify-submit) (define-endpoint ("/contact-us" :method :get) () .contact-us) (define-endpoint ("/contact-us/view" :method :get) () .contact-us-view) (define-endpoint ("/contact-us/email" :method :post) (&post (first_name :parameter-type 'string) (last_name :parameter-type 'string) (email :parameter-type 'string) (phone :parameter-type 'string) (comments :parameter-type 'string)) .contact-us-email) @@ -204,3 +189,11 @@ (define-endpoint ("/users/modify/submit" :method :post) (&post (id :parameter-type 'integer) (role_groups :parameter-type 'string) (username :parameter-type 'string) (first_name :parameter-type 'string) (last_name :parameter-type 'string) (email :parameter-type 'string) (phone :parameter-type 'string)) .users-modify-submit) (define-endpoint ("/users/toggle-active" :method :post) (&post (id :parameter-type 'integer)) .users-toggle-active) (define-endpoint ("/users/delete/:id" :method :delete) (&path (id 'integer)) .users-delete) + +(define-endpoint ("/resumes" :method :get) () .resumes) +(define-endpoint ("/resumes/view" :method :get) () .resumes-view) +(define-endpoint ("/resumes/add" :method :post) () .resumes-add) +(define-endpoint ("/resumes/add/submit" :method :post) (&post (name :parameter-type 'string)) .resumes-add-submit) +(define-endpoint ("/resumes/modify" :method :post) (&post (id :parameter-type 'integer)) .resumes-modify) +(define-endpoint ("/resumes/modify/submit" :method :post) (&post (id :parameter-type 'integer) (name :parameter-type 'string)) .resumes-modify-submit) +(define-endpoint ("/resumes/delete/:id" :method :delete) (&path (id 'integer)) .resumes-delete) diff --git a/lisp/webapps/resume/static/css/stylesheet.css b/lisp/webapps/resume/static/css/stylesheet.css index 659aa54..0589853 100644 --- a/lisp/webapps/resume/static/css/stylesheet.css +++ b/lisp/webapps/resume/static/css/stylesheet.css @@ -1,16 +1,20 @@ body { - background-color: #161012; + background-color: black; color: white; font-size: 14px; } hr { - border: 1px #ffd081 solid; + border: 1px #404040 solid; width: 200px; } a { - color: #ffd081; + color: lightblue; +} + +a:hover { + color: lightgreen; } .container-fluid { @@ -31,13 +35,13 @@ a { .nav-pills { .nav-link.active, .show > .nav-link { - background-color: #59200e; + background-color: #404040; } } .well { - background-color: #2c180d; - border: 1px black solid; + background-color: #202020; + border: 1px #404040 solid; text-align: center; } @@ -45,41 +49,39 @@ a { .btn-primary, .dropdown-toggle { color: white; - background-color: #59200e; - border-color: #59200e; + background-color: #202020; + border-color: #404040; &:hover { color: white; - background-color: #59200e; - border-color: #59200e; + background-color: #202020; + border-color: #404040; } } .show > .btn-primary.dropdown-toggle { color: white; - background-color: #59200e; - border-color: #59200e; + background-color: #202020; + border-color: #404040; } .dropdown-menu { color: white; - background-color: #59200e; + background-color: #202020; } .dropdown-item { color: white; - background-color: #59200e; + background-color: #202020; } .banner { height: 90px; - background-image: url("/static/images/violin-strip.jpg"); - background-repeat: repeat; - background-position: left; + background-color: black; } .banner-title { width: *; - color: #59200e; + color: white; font-size: 28px; font-weight: bold; text-align: center; @@ -93,18 +95,19 @@ a { .form-control { width: 600px; - background-color: #ffd081; + color: #ffffff; + background-color: #202020; } .contact-info { - color: #59200e; + color: #202020; font-size: 15px; font-weight: bold; line-height: 0.4; } .modal-content { - background-color: #161012; + background-color: #202020; } #body { diff --git a/sql/functions/resume.insert_resume.sql b/sql/functions/resume.insert_resume.sql new file mode 100644 index 0000000..eb56897 --- /dev/null +++ b/sql/functions/resume.insert_resume.sql @@ -0,0 +1,30 @@ +drop function resume.insert_resume ( + p_user_id int, + p_name character varying +); + +create or replace function resume.insert_resume ( + p_user_id int, + p_name character varying +) +returns bigint +as $$ +declare + l_id bigint; +begin + insert into resume.resume ( + user_id, + name + ) values ( + p_user_id, + p_name + ); + + select id into l_id + from resume.resume + where user_id = p_user_id + and name = p_name; + + return l_id; +end; +$$ language plpgsql; diff --git a/sql/tables/resume.contactinfo.sql b/sql/tables/resume.contactinfo.sql index 85bcd2c..47eb1c7 100644 --- a/sql/tables/resume.contactinfo.sql +++ b/sql/tables/resume.contactinfo.sql @@ -4,7 +4,7 @@ create table resume.contactinfo ( id serial8 primary key, address character varying(255) not null, city character varying(255) not null, - state int not null references resume.states(id), + state_id int not null references resume.states(id), phone character varying(255) not null, email character varying(255) not null, visastatus_id int not null references resume.visastatus(id) diff --git a/sql/tables/resume.resume.sql b/sql/tables/resume.resume.sql index 2d3c8bf..dd34db1 100644 --- a/sql/tables/resume.resume.sql +++ b/sql/tables/resume.resume.sql @@ -3,5 +3,6 @@ drop table resume.resume cascade; create table resume.resume ( id serial8 primary key, user_id int not null references auth.users(id), - name character varying(255) not null + name character varying(255) not null, + unique (user_id, name) ); -- cgit v1.3