Skip to content

Commit

Permalink
Modal implementation on habilitation validation page
Browse files Browse the repository at this point in the history
  • Loading branch information
MarjolaineLB authored and christophehenry committed Oct 30, 2024
1 parent ea57c34 commit 353cb2e
Show file tree
Hide file tree
Showing 36 changed files with 1,121 additions and 413 deletions.
5 changes: 5 additions & 0 deletions aidants_connect/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
path("", include("aidants_connect_web.urls")),
path("habilitation/", include("aidants_connect_habilitation.urls")),
path("", include("aidants_connect_pico_cms.urls")),
# APIs
path(
"api/habilitation/",
include("aidants_connect_habilitation.api.urls"),
),
]

if "test" in sys.argv:
Expand Down
47 changes: 43 additions & 4 deletions aidants_connect_common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
from django.db.models import Choices, IntegerChoices, TextChoices
from django.db.models.enums import ChoicesMeta as DjangoChoicesMeta
from django.utils.functional import Promise, classproperty
from django.utils.safestring import mark_safe
from django.utils.timezone import now
from django.utils.version import PY311

if PY311:
from enum import property as enum_property
else:
from types import DynamicClassAttribute as enum_property

__all__ = [
"DictChoices",
Expand Down Expand Up @@ -253,13 +260,45 @@ class RequestOriginConstants(IntegerChoices):

class RequestStatusConstants(TextChoicesEnum):
NEW = "Brouillon"
AC_VALIDATION_PROCESSING = "En attente de validation par Aidants Connect"
VALIDATED = "Validée"
REFUSED = "Refusée"
AC_VALIDATION_PROCESSING = mark_safe(
"En attente de validation d’éligibilité avant inscription en "
"formation des aidants"
)
VALIDATED = "Éligibilité validée"
REFUSED = "Éligibilité Refusée"
CLOSED = "Clôturée"
CHANGES_REQUIRED = "Modifications demandées"
CHANGES_REQUIRED = "Demande de modifications par l’équipe Aidants Connect"
CHANGES_PROPOSED = "Modifications proposées par Aidants Connect"

@enum_property
def description(self):
match self:
case self.AC_VALIDATION_PROCESSING:
return mark_safe(
"<p>Votre demande d’habilitation est en cours d’instruction "
"par nos équipes. Vous serez prochainement notifié de la "
"décision de nos équipes concernant votre dossier."
)
case self.VALIDATED:
return mark_safe(
"<p>Félicitations, votre demande d’habilitation a été acceptée par "
"Aidants Connect !</p>"
"<p>Vous pouvez désormais inscrire le référent sur un webinaire "
"d’information dédié aux référents et inscrire les aidants en "
"formation.</p>"
)
case self.CHANGES_REQUIRED:
return mark_safe(
"<p>L'équipe Aidants Connect a étudié votre demande d’habilitation "
"et souhaite que vous y apportiez des modifications. N’oubliez pas "
"de valider à nouveau votre demande d’habilitation en cliquant sur "
"le bouton « Soumettre la demande » pour que l'équipe Aidants "
"Connect prenne en compte vos modifications et valide votre "
"demande</p>"
)
case _:
return ""

@classproperty
def aidant_registrable(cls):
"""Statuses that allow to add new aidants to an habilitation request"""
Expand Down
1 change: 0 additions & 1 deletion aidants_connect_common/static/css/dsfr-theme-tac.min.css

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ details.request-card-details summary {
height: 100%;
min-height: 4rem;
position: relative;
z-index: 10;
z-index: calc(var(--ground) + 510);
}

details.request-card-details[open] .details-summary-header {
Expand All @@ -28,10 +28,4 @@ details.request-card-details .details-content {
padding: inherit;
position: absolute;
width: 100%;
z-index: 0;
}

details.request-card-details .details-content .fr-btns-group .fr-btn {
margin-bottom: 0;
margin-top: 0;
}
2 changes: 1 addition & 1 deletion aidants_connect_common/static/css/main.css

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import {Controller} from "Stimulus";
import {aidantsConnectApplicationReady, BaseController} from "AidantsConnectApplication";

const MODAL_STATES = Object.freeze({
IDLE: 1,
LOADING: 2,
ERROR: 3,
})

/**
* @property {HTMLElement} contentTarget
* @property {HTMLElement} titleTarget
* @property {HTMLElement} footerTarget
* @property {HTMLImageElement} loaderTarget
* @property {HTMLElement} errorTarget
* @property {HTMLElement[]} footerButtonTargets
* @property {ProfileEditCard[]} profileEditCardOutlets
* @property {String} idValue
* @property {Number} stateValue
* @property {Boolean} displayValue
*/
class ProfileEditModal extends BaseController {
static outlets = ["profile-edit-card"]
static targets = ["title", "content", "footer", "loader", "error", "footerButton"]
static values = {
id: {type: String, default: undefined},
state: {type: Number, default: MODAL_STATES.IDLE},
display: {type: Boolean, default: false},
}

initialize () {
/** @type {{String: ProfileEditCard}} */
this.profileEditCards = {}
}

stateValueChanged (state) {
this.setContentVisibility(state)
this.setFooterVisibility(state)
this.setErrorVisibility(state)
this.setFooterButtonsVisibility(state)
}

displayValueChanged (display) {
if (display) {
dsfr(this.element).modal.disclose();
} else {
dsfr(this.element).modal.conceal();
}
}

/**@param {ProfileEditCard} outlet */
profileEditCardOutletConnected (outlet) {
this.profileEditCards[outlet.idValue] = outlet
}

/**@param {ProfileEditCard} outlet */
profileEditCardOutletDisconnected (outlet) {
delete this.profileEditCards[outlet.idValue]
}

/**
* @param {String} title Modal title
* @param {String|Promise<String>} content Modal content as HTML string or promise that resolves into HTML string
* @param {String} id Id of the item being edited
*/
createAndShow ({title = undefined, content, id} = {}) {
this.idValue = id
if (content instanceof Promise) {
this.stateValue = MODAL_STATES.LOADING;
} else {
content = Promise.resolve(content);
}

content.then(html => {
this.contentTarget.innerHTML = html;
this.idValue = id;
this.stateValue = MODAL_STATES.IDLE;
}).catch(() => {
this.stateValue = MODAL_STATES.ERROR;
})

this.displayValue = true
}

onDelete (evt) {
if (this.idValue === undefined || this.stateValue !== MODAL_STATES.IDLE) {
return
}

const controller = this.profileEditCards[this.idValue]
if (controller) {

}
}

onValidate (evt) {
if (this.idValue === undefined || this.stateValue !== MODAL_STATES.IDLE) {
return
}

const controller = this.profileEditCards[this.idValue]
if (!controller) {
this.displayValue = false;
return;
}

this.stateValue = MODAL_STATES.LOADING
controller
.validate()
.then(result => {
// All is well; empty modal and hide
if (result === undefined) {
// Prevent from acting on this record after action success
this.idValue = undefined
this.displayValue = false
} else {
this.contentTarget.innerHTML = result
this.stateValue = MODAL_STATES.IDLE
}
})
.catch(err => {
console.error(err)
this.stateValue = MODAL_STATES.ERROR;
})

}

onConceal (evt) {
// Reflect changes produced by clicking outside the dialog
this.displayValue = false
setTimeout(() => {
// Modify display after modal has disapeared
this.contentTarget.innerHTML = ""
this.stateValue = MODAL_STATES.IDLE
}, 300)
}

// region elements visibility
setContentVisibility (state) {
if (state === MODAL_STATES.IDLE) {
this.showElement(this.contentTarget);
} else {
this.hideElement(this.contentTarget);
}
}

setFooterVisibility (state) {
if (state === MODAL_STATES.IDLE) {
this.showElement(this.footerTarget);
} else {
this.hideElement(this.footerTarget);
}
}

setLoaderVisibility (state) {
if (state === MODAL_STATES.LOADING) {
this.showElement(this.loaderTarget);
} else {
this.hideElement(this.loaderTarget);
}
}

setErrorVisibility (state) {
if (state === MODAL_STATES.ERROR) {
this.showElement(this.errorTarget);
} else {
this.hideElement(this.errorTarget);
}
}

setFooterButtonsVisibility (state) {
this.footerButtonTargets.forEach(it => {
debugger
if (state === MODAL_STATES.IDLE) {
it.removeAttribute("disabled")
} else {
it.setAttribute("disabled", "disabled")
}
})
}
//endregion
}

/**
* @property {String} idValue
* @property {String} enpointValue
* @property {ProfileEditModal} profileEditModalOutlet
*/
class ProfileEditCard extends Controller {
static values = {id: String, enpoint: String}
static outlets = ["profile-edit-modal"]

onEdit (elt) {
this.profileEditModalOutlet.createAndShow({
id: this.idValue,
content: fetch(this.enpointValue).then(async response => {
if (!response.ok) {
throw response.statusText
}
return await response.text();
})
});
}

/** @returns {Promise<String|undefined>} */
async validate () {
return fetch(this.enpointValue, {
body: new FormData(this.profileEditModalOutlet.contentTarget.querySelector("form")),
method: "POST",
}).then(async response => {
// Error in the form, we return the HTML so that modal can display it
if (response.status === 422) {
return await response.text()
}

// Other error. Modal should display error message
if (!response.ok) {
throw response.statusText
}

// All is good. Update the <details> and return undefined to let the modal know it shoud close
this.element.outerHTML = await response.text();
return undefined
})
}

async delete () {
}
}

aidantsConnectApplicationReady.then(/** @param {Stimulus.Application} app */ app => app.register("profile-edit-modal", ProfileEditModal))
aidantsConnectApplicationReady.then(/** @param {Stimulus.Application} app */ app => app.register("profile-edit-card", ProfileEditCard))
3 changes: 2 additions & 1 deletion aidants_connect_common/static/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ body {

ul, ol {
margin: var(--text-spacing);

p:has(+&) {
margin-bottom: var(--xl-block);
}
Expand Down Expand Up @@ -146,7 +147,7 @@ p:last-child {
height: 100%;
}

.fr-btn.fr-btn--warning {
.fr-btn.fr-btn--warning:not(:disabled) {
--hover-tint: var(--hover);
--idle: transparent;
--hover: var(--background-action-high-warning-hover);
Expand Down
4 changes: 4 additions & 0 deletions aidants_connect_common/templates/forms/form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<form method="post" action="{{ action }}">
{% csrf_token %}
{{ form }}
</form>
Loading

0 comments on commit 353cb2e

Please sign in to comment.