From fb9b65b13886a7d500c07e9643b2c3e03b22b0c6 Mon Sep 17 00:00:00 2001 From: SimonGschnell Date: Mon, 22 Jan 2024 14:45:32 +0100 Subject: [PATCH] changes how to display the hr table with all the user update requests and the update request table in the profil view, also adds some new columns to the cis_profil_update table to store the status, status date and status message --- application/controllers/Cis/Profil.php | 4 +- application/controllers/Cis/ProfilUpdate.php | 51 ++++-- .../models/person/Profil_change_model.php | 9 +- application/views/Cis/Profil.php | 2 +- application/views/Cis/ProfilUpdate.php | 2 +- public/css/components/FormUnderline.css | 35 ++++ public/css/components/Profil.css | 34 ---- public/js/apps/Cis/ProfilUpdateRequests.js | 92 +++++++--- public/js/apps/api/fhcapifactory.js | 4 +- public/js/apps/api/profilUpdate.js | 20 +++ public/js/apps/api/userdata.js | 6 - .../ProfilComponents/FetchProfilUpdates.js | 41 ++++- .../Cis/ProfilUpdate/AcceptDenyUpdate.js | 160 ++++++++++++++++++ system/dbupdate_3.4/25999_C4_ma0594.php | 4 + 14 files changed, 372 insertions(+), 92 deletions(-) create mode 100644 public/css/components/FormUnderline.css create mode 100644 public/js/apps/api/profilUpdate.js create mode 100644 public/js/components/Cis/ProfilUpdate/AcceptDenyUpdate.js diff --git a/application/controllers/Cis/Profil.php b/application/controllers/Cis/Profil.php index 6789de7b8..c9801bbb5 100644 --- a/application/controllers/Cis/Profil.php +++ b/application/controllers/Cis/Profil.php @@ -94,7 +94,7 @@ class Profil extends Auth_Controller $payload = $json->payload; $type = isset($json->payload->kontakt_id)? "kontakt_id" : "adresse_id"; - $data = ["topic"=>$json->topic,"uid" => $this->uid, "requested_change" => json_encode($payload), "change_timestamp" => "NOW()" ]; + $data = ["topic"=>$json->topic,"uid" => $this->uid, "requested_change" => json_encode($payload), "change_timestamp" => "NOW()","status"=>"pending" ]; //? loops over all updateRequests from a user to validate if the new request is valid $res = $this->ProfilChangeModel->loadWhere(["uid"=>$this->uid]); @@ -148,7 +148,7 @@ class Profil extends Auth_Controller $json = json_decode($this->input->raw_input_stream); - $data = ["topic"=>$json->topic,"uid" => $this->uid, "requested_change" => json_encode($json->payload), "change_timestamp" => "NOW()" ]; + $data = ["topic"=>$json->topic,"uid" => $this->uid, "requested_change" => json_encode($json->payload), "change_timestamp" => "NOW()","status"=>"pending" ]; //? gets all the requested changes from a user diff --git a/application/controllers/Cis/ProfilUpdate.php b/application/controllers/Cis/ProfilUpdate.php index 2ee7368be..fcbda43e2 100644 --- a/application/controllers/Cis/ProfilUpdate.php +++ b/application/controllers/Cis/ProfilUpdate.php @@ -13,6 +13,8 @@ class ProfilUpdate extends Auth_Controller parent::__construct([ 'index' => ['student/anrechnung_beantragen:r', 'user:r'], // TODO(chris): permissions? 'getAllRequests' => ['student/anrechnung_beantragen:r', 'user:r'], + 'acceptProfilRequest'=>['user:r'], + 'denyProfilRequest'=>['user:r'], ]); //? put the uid and pid inside the controller to reuse in controller @@ -28,22 +30,39 @@ class ProfilUpdate extends Auth_Controller } public function getAllRequests(){ - - $res = $this->ProfilChangeModel->load(); - if(isError($res)){ - // catch exception - echo $res->retval->data; - return; - }else{ - if(hasData($res)){ - $res = getData($res); - foreach($res as $element){ - $element->change_timestamp = date_create($element->change_timestamp)->format('d/m/Y H:i'); - } - }else{ - $res = null; - } - } + $res = $this->ProfilChangeModel->getProfilUpdate(); + $res = hasData($res)? getData($res) : null; echo json_encode($res); } + + public function acceptProfilRequest(){ + $_POST = json_decode($this->input->raw_input_stream,true); + + $id = $this->input->post('requestID',true); + + if(isset($id)){ + $res =$this->ProfilChangeModel->update([$id], ["status"=>"accepted","status_timestamp"=>"NOW()"]); + echo json_encode($res); + } + + } + + public function denyProfilRequest(){ + $_POST = json_decode($this->input->raw_input_stream,true); + + $id = $this->input->post('requestID',true); + + if(isset($id)){ + var_dump($id); + //! instead of deleting the rejected profil update, the status of the db entry is set to rejected + //$res = $this->ProfilChangeModel->delete([$id]); + + + $res = $this->ProfilChangeModel->update([$id],["status"=>"rejected","status_timestamp"=>"NOW()"]); + echo json_encode($res); + + } + + + } } \ No newline at end of file diff --git a/application/models/person/Profil_change_model.php b/application/models/person/Profil_change_model.php index fb48e4726..2f33068c6 100644 --- a/application/models/person/Profil_change_model.php +++ b/application/models/person/Profil_change_model.php @@ -30,8 +30,13 @@ class Profil_change_model extends DB_Model * returns a profil update with id * returns all profil updates if id is set to null */ - public function getProfilUpdate($uid,$id=null){ - $whereClause = ["uid"=>$uid]; + public function getProfilUpdate($uid=null,$id=null){ + $whereClause=[]; + + if(!is_null($uid)){ + $whereClause['uid']=$uid; + } + //? if(!is_null($id)){ $whereClause['profil_update_id']=$id; } diff --git a/application/views/Cis/Profil.php b/application/views/Cis/Profil.php index 9e09bea62..99d2c0ae4 100644 --- a/application/views/Cis/Profil.php +++ b/application/views/Cis/Profil.php @@ -3,7 +3,7 @@ $includesArray = array( 'title' => 'Stundenplan', 'customJSModules' => ['public/js/apps/Cis/Profil.js'], 'tabulator5' => true, - 'customCSSs' => ['public/css/components/calendar.css', 'public/css/components/FilterComponent.css','public/css/components/Profil.css'], + 'customCSSs' => ['public/css/components/calendar.css', 'public/css/components/FilterComponent.css','public/css/components/Profil.css','public/css/components/FormUnderline.css'], ); diff --git a/application/views/Cis/ProfilUpdate.php b/application/views/Cis/ProfilUpdate.php index f55fd4dc1..1c50bf458 100644 --- a/application/views/Cis/ProfilUpdate.php +++ b/application/views/Cis/ProfilUpdate.php @@ -2,7 +2,7 @@ $includesArray = ['title'=> 'Profil Änderungen', 'customJSModules'=> ['public/js/apps/Cis/ProfilUpdateRequests.js'], 'tabulator5'=> true, - 'customCSSs'=>['public/css/components/FilterComponent.css'], + 'customCSSs'=>['public/css/components/FilterComponent.css','public/css/components/FormUnderline.css'], ]; $this->load->view('templates/CISHTML-Header',$includesArray); diff --git a/public/css/components/FormUnderline.css b/public/css/components/FormUnderline.css new file mode 100644 index 000000000..6b3092b49 --- /dev/null +++ b/public/css/components/FormUnderline.css @@ -0,0 +1,35 @@ +/* FORM UNDERLINE +*/ +.form-underline{ + display: flex; + flex-direction: column; + justify-content: center; + align-content: space-between; + + } + + + + .form-underline .form-underline-content{ + border-width: 1px; + border-color: transparent transparent #dee2e6 transparent; + border-style: solid; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + + /*optional not wrapping text that is horizontally scrollable and does not display a scrollbar */ + white-space: nowrap; + overflow: auto; + scrollbar-width: none; + + } + + .form-underline .form-underline-content::-webkit-scrollbar { + display: none; + } + + .form-underline .form-underline-titel{ + opacity: 0.65; + font-size: .85rem; + padding-left: 0.5rem; + } \ No newline at end of file diff --git a/public/css/components/Profil.css b/public/css/components/Profil.css index 4ca104dc8..31adc7fff 100644 --- a/public/css/components/Profil.css +++ b/public/css/components/Profil.css @@ -41,41 +41,7 @@ } -/* FORM UNDERLINE -*/ -.form-underline{ - display: flex; - flex-direction: column; - justify-content: center; - align-content: space-between; -} - - - -.form-underline .form-underline-content{ - border-width: 1px; - border-color: transparent transparent #dee2e6 transparent; - border-style: solid; - padding-bottom: 0.25rem; - padding-left: 0.5rem; - - /*optional not wrapping text that is horizontally scrollable and does not display a scrollbar */ - white-space: nowrap; - overflow: auto; - scrollbar-width: none; - -} - -.form-underline .form-underline-content::-webkit-scrollbar { - display: none; -} - -.form-underline .form-underline-titel{ - opacity: 0.65; - font-size: .85rem; - padding-left: 0.5rem; -} .btn-circle { width: 30px; diff --git a/public/js/apps/Cis/ProfilUpdateRequests.js b/public/js/apps/Cis/ProfilUpdateRequests.js index 65b996f3c..5f6f1b31c 100644 --- a/public/js/apps/Cis/ProfilUpdateRequests.js +++ b/public/js/apps/Cis/ProfilUpdateRequests.js @@ -1,53 +1,100 @@ import fhcapifactory from "../api/fhcapifactory.js"; -import {CoreFilterCmpt} from '../../components/filter/Filter.js' +import {CoreFilterCmpt} from "../../components/filter/Filter.js"; +import AcceptDenyUpdate from "../../components/Cis/ProfilUpdate/AcceptDenyUpdate.js"; Vue.$fhcapi = fhcapifactory; -/* -data: [ - { - uid: "", - profil_changes: "", - change_timestamp: "", - }, -], */ + + +let printIcon= function(cell, formatterParams, onRendered){ //plain text value + return ""; +}; const app = Vue.createApp({ components:{ CoreFilterCmpt, + }, data(){ return{ + profil_updates_table_options:{ ajaxURL:FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/Cis/ProfilUpdate/getAllRequests', height:300, layout:'fitColumns', - responsiveLayout: "collapse", - responsiveLayoutCollapseUseFormatters:false, - responsiveLayoutCollapseFormatter:this.collapseFormatter, columns: [ { - title: "Uid", - field: "uid", + title: "Topic", + field: "topic", + resizable: true, minWidth: 200, - responsive:0, + //responsive:0, }, { - title: "Update", - field: "profil_changes", - minWidth: 10000, - responsive:3, + title: "UID", + field: "uid", + minWidth: 200, + //responsive:0, }, + { title: "Date", field: "change_timestamp", resizable: true, minWidth: 200, - responsive:0, + //responsive:0, }, + { + title: "Status", + field: "status", + hozAlign:'center', + formatter: function(cell,para){ + let res =Object.getPrototypeOf(cell); + //console.log(res); + + switch(cell.getValue()){ + case "pending": return " pending"; + case "accepted": return " accepted"; + case "rejected": return " rejected"; + default: return "

default

"; + } + }, + + resizable: true, + minWidth: 200, + //responsive:0, + }, + { + title: "View", + formatter:printIcon, + resizable: true, + minWidth: 200, + hozAlign: 'center', + cellClick:(e, cell)=>{ + //! function that is called when clicking on a row in the table + + let cellData = cell.getRow().getData(); + AcceptDenyUpdate.popup({value:cellData}).then(res=>{ + console.log("res of the modal: ",res); + //? refetches the data, if any request was denied or accepted + //* setData will call the ajaxURL again to refresh the data + this.$refs.UpdatesTable.tabulator.setData(); + }).catch(e=>{ + //? catches the rejected Promise if the result of the modal was falsy + console.log("catch of the modal: ",e); + }); + + }, + //responsive:0, + }, + + ], }, } + }, + computed:{ + }, methods:{ sideMenuFunction: function(){ @@ -82,7 +129,9 @@ const app = Vue.createApp({ }, created(){ - + Vue.$fhcapi.ProfilUpdate.getProfilUpdateRequest().then((res)=>{ + console.log(res.data); + }) }, @@ -93,6 +142,7 @@ const app = Vue.createApp({ template:`
+
`, diff --git a/public/js/apps/api/fhcapifactory.js b/public/js/apps/api/fhcapifactory.js index f75b03482..9845d2fc5 100644 --- a/public/js/apps/api/fhcapifactory.js +++ b/public/js/apps/api/fhcapifactory.js @@ -1,7 +1,9 @@ import Search from "./search.js"; import UserData from "./userdata.js"; +import ProfilUpdate from "./profilUpdate.js" export default { "Search": Search, - "UserData": UserData + "UserData": UserData, + "ProfilUpdate": ProfilUpdate, }; diff --git a/public/js/apps/api/profilUpdate.js b/public/js/apps/api/profilUpdate.js new file mode 100644 index 000000000..b9aa01731 --- /dev/null +++ b/public/js/apps/api/profilUpdate.js @@ -0,0 +1,20 @@ +export default { + + //! API calls for profil update requests + + getProfilUpdateRequest: function(){ + const url = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/Cis/ProfilUpdate/getAllRequests'; + return axios.get(url); + }, + + acceptProfilRequest: function(requestID){ + const url = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/Cis/ProfilUpdate/acceptProfilRequest'; + return axios.post(url,{requestID: requestID}); + }, + + denyProfilRequest: function(requestID){ + const url = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/Cis/ProfilUpdate/denyProfilRequest'; + return axios.post(url,{requestID: requestID}); + } + +} \ No newline at end of file diff --git a/public/js/apps/api/userdata.js b/public/js/apps/api/userdata.js index 47df445b4..6c881393d 100644 --- a/public/js/apps/api/userdata.js +++ b/public/js/apps/api/userdata.js @@ -56,12 +56,6 @@ export default { }, - //! API Calls for Profil Update View - - getProfilUpdateRequest: function(){ - const url = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/Cis/ProfilUpdate/getAllRequests'; - return axios.get(url); - } diff --git a/public/js/components/Cis/Profil/ProfilComponents/FetchProfilUpdates.js b/public/js/components/Cis/Profil/ProfilComponents/FetchProfilUpdates.js index ff42454fc..5af4a89ae 100644 --- a/public/js/components/Cis/Profil/ProfilComponents/FetchProfilUpdates.js +++ b/public/js/components/Cis/Profil/ProfilComponents/FetchProfilUpdates.js @@ -33,12 +33,13 @@ export default { case "Private Adressen" : return "EditAdresse"; break; case "Add Adressen" : return "EditAdresse"; break; case "Add Kontakte" : return "EditKontakt"; break; - case "Delete Adressen" : return "EditAdresse"; break; - case "Delete Kontakte" : return "EditKontakt"; break; + case "Delete Adressen" : return "Adresse"; break; + case "Delete Kontakte" : return "Kontakt"; break; default: return "text_input"; break; } }, openModal(updateRequest) { + console.log(JSON.stringify(updateRequest)); let view = this.getView(updateRequest.topic); let content =null; @@ -104,18 +105,42 @@ export default { Topic + Status Date of Request - Bearbeiten - Löschen + + Action + + - + + {{item.topic}} + {{item.status}} {{item.change_timestamp}} - {{item.requested_change.adr_typ?item.requested_change.adr_typ:item.requested_change.kontakt}} - - + + + + + + + + + diff --git a/public/js/components/Cis/ProfilUpdate/AcceptDenyUpdate.js b/public/js/components/Cis/ProfilUpdate/AcceptDenyUpdate.js new file mode 100644 index 000000000..ee8485bf7 --- /dev/null +++ b/public/js/components/Cis/ProfilUpdate/AcceptDenyUpdate.js @@ -0,0 +1,160 @@ +import BsModal from "../../Bootstrap/Modal.js"; +import Alert from "../../Bootstrap/Alert.js"; +import Kontakt from "../Profil/ProfilComponents/Kontakt.js"; +import Adresse from "../Profil/ProfilComponents/Adresse.js"; + + +export default { + components: { + BsModal, + Alert, + Kontakt, + Adresse, + + }, + mixins: [BsModal], + props: { + title:{ + type:String, + default:"Profil Update Request" + }, + value: { + type:Object, + }, + /* + * NOTE(chris): + * Hack to expose in "emits" declared events to $props which we use + * in the v-bind directive to forward all events. + * @see: https://github.com/vuejs/core/issues/3432 + */ + onHideBsModal: Function, + onHiddenBsModal: Function, + onHidePreventedBsModal: Function, + onShowBsModal: Function, + onShownBsModal: Function, + }, + data() { + return { + data: this.value, + //? result is returned from the Promise when the modal is closed + result: false, + info: null, + } + }, + + methods: { + acceptRequest: function(){ + console.log(this.data.profil_update_id); + Vue.$fhcapi.ProfilUpdate.acceptProfilRequest(this.data.profil_update_id).then(res =>{ + console.log("res",res); + console.log("res.data",res.data); + this.result = true; + }) + this.hide(); + }, + + denyRequest: function(){ + console.log(this.data.profil_update_id); + Vue.$fhcapi.ProfilUpdate.denyProfilRequest(this.data.profil_update_id).then(res =>{ + console.log("res",res); + console.log("res.data",res.data); + this.result = true; + }) + this.hide(); + }, + + submitProfilChange(){ + //TODO: check if the updated value is different from the original value before submitting the request + if(false){ + + //? inserts new row in public.tbl_cis_profil_update + //* calls the update api call if an update field is present in the data that was passed to the module + Vue.$fhcapi.UserData[this.editData.update?'updateProfilRequest':'insertProfilRequest'](this.topic,this.profilUpdate).then((res)=>{ + + if(res.data.error == 0){ + this.result= true; + this.hide(); + Alert.popup("Ihre Anfrage wurde erfolgreich gesendet. Bitte warten Sie, während sich das Team um Ihre Anfrage kümmert."); + }else{ + this.result= false; + this.hide(); + Alert.popup("Ein Fehler ist aufgetreten: "+ JSON.stringify(res.data.retval)); + } + + }); + } + }, + }, + computed: { + getComponentView: function(){ + + if(this.data.topic.toLowerCase().includes("kontakt")){ + return "kontakt"; + }else if (this.data.topic.toLowerCase().includes("adresse")){ + return "adresse"; + }else{ + return "text_input"; + } + }, + }, + created() { + console.log("data passed as prop",this.data); + + }, + mounted() { + this.modal = this.$refs.modalContainer.modal; + }, + popup(options) { + return BsModal.popup.bind(this)(null, options); + }, + template: ` + + + + + + + + + + + `, +}; diff --git a/system/dbupdate_3.4/25999_C4_ma0594.php b/system/dbupdate_3.4/25999_C4_ma0594.php index 4d694c4c2..151a727b2 100644 --- a/system/dbupdate_3.4/25999_C4_ma0594.php +++ b/system/dbupdate_3.4/25999_C4_ma0594.php @@ -8,6 +8,9 @@ uid VARCHAR(32) NOT NULL, requested_change jsonb NOT NULL, change_timestamp TIMESTAMP NOT NULL, + status VARCHAR(32) NOT NULL, + status_timestamp TIMESTAMP NULL, + status_message TEXT NULL, CONSTRAINT tbl_cis_profil_update_pk PRIMARY KEY(profil_update_id), CONSTRAINT tbl_cis_profil_update_fk FOREIGN KEY(uid) REFERENCES public.tbl_benutzer(uid) ); @@ -19,6 +22,7 @@ CACHE 1; ALTER TABLE public.tbl_cis_profil_update ALTER COLUMN profil_update_id SET DEFAULT nextval('public.tbl_cis_profil_update_id_seq'); + ALTER TABLE public.tbl_cis_profil_update ADD CONSTRAINT tbl_cis_profil_update_restricted_status CHECK (status IN ('pending','accepted','rejected')); --ALTER TABLE public.tbl_cis_profil_update ADD CONSTRAINT cis_profil_udpate_topic_uid_unique UNIQUE (uid,topic); GRANT SELECT, INSERT, UPDATE, DELETE ON public.tbl_cis_profil_update TO vilesci;