diff --git a/js/App.hx b/js/App.hx index 0b1372a..993e52a 100755 --- a/js/App.hx +++ b/js/App.hx @@ -1,33 +1,33 @@ -//React lib +// React lib import react.ReactMacro.jsx; import react.ReactDOM; import react.*; import react.router.*; -//custom components +// custom components import react.order.*; import react.product.*; import react.store.*; import react.map.*; import react.user.*; -//require bootstrap JS since it's bundled with browserify -//@:jsRequire('bootstrap') extern class Bootstrap{} -//@:jsRequire('jquery') extern class JQ extends js.jquery.JQuery{} - +// require bootstrap JS since it's bundled with browserify +// @:jsRequire('bootstrap') extern class Bootstrap{} +// @:jsRequire('jquery') extern class JQ extends js.jquery.JQuery{} class App { + public static var instance:App; - public static var instance : App; - public var LANG : String; - public var currency : String; //currency symbol like € or $ - public var t : sugoi.i18n.GetText;//gettext translator + public var LANG:String; + public var currency:String; // currency symbol like € or $ + public var t:sugoi.i18n.GetText; // gettext translator - //i dont want to use redux now... saved state from react.OrderBox - public static var SAVED_ORDER_STATE : Dynamic; + // i dont want to use redux now... saved state from react.OrderBox + public static var SAVED_ORDER_STATE:Dynamic; - function new(?lang="fr",?currency="€") { - //singleton + function new(?lang = "fr", ?currency = "€") { + // singleton instance = this; - if(lang!=null) this.LANG = lang; + if (lang != null) + this.LANG = lang; this.currency = currency; } @@ -39,7 +39,7 @@ class App { return new js.JQuery(r); } - public static inline function jq(r:Dynamic):js.jquery.JQuery{ + public static inline function jq(r:Dynamic):js.jquery.JQuery { return new js.jquery.JQuery(r); } @@ -47,8 +47,7 @@ class App { * The JS App will be available as "_" in the document. */ public static function main() { - - //untyped js.Browser.window.$ = js.Lib.require("jQuery"); + // untyped js.Browser.window.$ = js.Lib.require("jQuery"); untyped js.Browser.window._ = new App(); } @@ -56,32 +55,31 @@ class App { return new ShopCart(); } - public function getTagger(cid:Int ) { + public function getTagger(cid:Int) { return new Tagger(cid); } public function getTuto(name:String, step:Int) { - new Tuto(name,step); + new Tuto(name, step); } - + /** * remove method for IE compat */ - public function remove(el:js.html.Element){ - if (el == null) return; + public function remove(el:js.html.Element) { + if (el == null) + return; el.parentElement.removeChild(el); } - - public function getVATBox(ttcprice:Float,currency:String,rates:String,vat:Float,formName:String){ - + + public function getVATBox(ttcprice:Float, currency:String, rates:String, vat:Float, formName:String) { var input = js.Browser.document.querySelector('form input[name="${formName}_price"]'); - - remove( js.Browser.document.querySelector('form input[name="${formName}_vat"]').parentElement.parentElement ); - - ReactDOM.render(jsx('<$VATBox ttc="$ttcprice" currency="$currency" vatRates="$rates" vat="$vat" formName="$formName"/>'), input.parentElement); - - //remove(input); - + + remove(js.Browser.document.querySelector('form input[name="${formName}_vat"]').parentElement.parentElement); + + ReactDOM.render(jsx('<$VATBox ttc="$ttcprice" currency="$currency" vatRates="$rates" vat="$vat" formName="$formName"/>'), input.parentElement); + + // remove(input); } /*public function getProductComposer(){ @@ -89,7 +87,6 @@ class App { //ReactDOM.render(jsx('<$ComposerApp/>'), js.Browser.document.getElementById("app")); //}); }*/ - /** * Removes the form element and replace it by a react js component * @param divId @@ -97,72 +94,71 @@ class App { * @param txpProductId * @param formName */ - public function getProductInput(divId:String, productName:String, txpProductId:String, formName:String ){ - + public function getProductInput(divId:String, productName:String, txpProductId:String, formName:String) { js.Browser.document.addEventListener("DOMContentLoaded", function(event) { + // dirty stuff to remove "real" input, and replace it by the react one + App.j("form input[name='" + formName + "_name']").parent().parent().remove(); + App.j("form select[name='" + formName + "_txpProductId']") + .parent() + .parent() + .remove(); - //dirty stuff to remove "real" input, and replace it by the react one - App.j("form input[name='"+formName+"_name']").parent().parent().remove(); - App.j("form select[name='" + formName+"_txpProductId']").parent().parent().remove(); + if (txpProductId == null) + txpProductId = ""; - if (txpProductId == null) txpProductId = ""; - - ReactDOM.render(jsx('<$ProductInput productName="$productName" txpProductId="$txpProductId" formName="$formName"/>'), js.Browser.document.getElementById(divId)); + ReactDOM.render(jsx('<$ProductInput productName="$productName" txpProductId="$txpProductId" formName="$formName"/>'), + js.Browser.document.getElementById(divId)); }); } - public function initReportHeader(){ - ReactDOM.render(jsx('<$ReportHeader />'), js.Browser.document.querySelector('div.reportHeaderContainer')); + public function initReportHeader() { + ReactDOM.render(jsx('<$ReportHeader />'), js.Browser.document.querySelector('div.reportHeaderContainer')); } - - public function initOrderBox(userId:Int, distributionId:Int, contractId:Int, contractType:Int, date:String, place:String, userName:String, currency:String, hasPayments:Bool,callbackUrl:String){ + public function initOrderBox(userId:Int, distributionId:Int, contractId:Int, contractType:Int, date:String, place:String, userName:String, + currency:String, hasPayments:Bool, callbackUrl:String) { untyped App.j("#myModal").modal(); var onValidate = function() js.Browser.location.href = callbackUrl; var node = js.Browser.document.querySelector('#myModal .modal-body'); - ReactDOM.unmountComponentAtNode(node); //the previous modal DOM element is still there, so we need to destroy it - ReactDOM.render(jsx('<$OrderBox userId="$userId" distributionId="$distributionId" - contractId="$contractId" contractType="$contractType" date="$date" place="$place" userName="$userName" - onValidate=$onValidate currency=$currency hasPayments=$hasPayments />'),node,postReact); - + ReactDOM.unmountComponentAtNode(node); // the previous modal DOM element is still there, so we need to destroy it + ReactDOM.render(jsx('<$OrderBox userId="$userId" distributionId="$distributionId" + contractId="$contractId" contractType="$contractType" date="$date" place="$place" userName="$userName" + onValidate=$onValidate currency=$currency hasPayments=$hasPayments />'), node, postReact); } - function postReact(){ + function postReact() { trace("post react"); - haxe.Timer.delay(function(){ + haxe.Timer.delay(function() { untyped jq('[data-toggle="tooltip"]').tooltip(); untyped jq('[data-toggle="popover"]').popover(); - },500); - + }, 500); } public static function roundTo(n:Float, r:Int):Float { - return Math.round(n * Math.pow(10,r)) / Math.pow(10,r) ; + return Math.round(n * Math.pow(10, r)) / Math.pow(10, r); } - /** * Ajax loads a page and display it in a modal window * @param url * @param title */ - public function overlay(url:String,?title,?large=true) { - - if (title != null) title = StringTools.urlDecode(title); + public function overlay(url:String, ?title, ?large = true) { + if (title != null) + title = StringTools.urlDecode(title); var r = new haxe.Http(url); r.onData = function(data) { - - //setup body and title + // setup body and title var m = App.j("#myModal"); m.find(".modal-body").html(data); - if (title != null) m.find(".modal-title").html(title); + if (title != null) + m.find(".modal-title").html(title); - if (!large) m.find(".modal-dialog").removeClass("modal-lg"); - - - untyped App.j('#myModal').modal(); //bootstrap 3 modal window + if (!large) + m.find(".modal-dialog").removeClass("modal-lg"); + untyped App.j('#myModal').modal(); // bootstrap 3 modal window } r.request(); } @@ -170,52 +166,65 @@ class App { /** * Displays a login box */ - public function loginBox(redirectUrl:String,?message:String,?phoneRequired=false) { + public function loginBox(redirectUrl:String, ?message:String, ?phoneRequired = false) { var m = App.j("#myModal"); m.find(".modal-title").html("S'identifier"); m.find(".modal-dialog").removeClass("modal-lg"); untyped m.modal(); - ReactDOM.render(jsx('<$LoginBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), js.Browser.document.querySelector('#myModal .modal-body')); + ReactDOM.render(jsx('<$LoginBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), + js.Browser.document.querySelector('#myModal .modal-body')); return false; } /** * Displays a sign up box */ - public function registerBox(redirectUrl:String,?message:String,?phoneRequired=false) { + public function registerBox(redirectUrl:String, ?message:String, ?phoneRequired = false) { var m = App.j("#myModal"); m.find(".modal-title").html("S'inscrire"); m.find(".modal-dialog").removeClass("modal-lg"); untyped m.modal(); - ReactDOM.render(jsx('<$RegisterBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), js.Browser.document.querySelector('#myModal .modal-body')); + ReactDOM.render(jsx('<$RegisterBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), + js.Browser.document.querySelector('#myModal .modal-body')); + return false; + } + + public function cookieConsentBox(redirectUrl:String, ?message:String, ?phoneRequired = false) { + js.Browser.console.log("cookieConsentBox should load modal..."); + var m = App.j("#myModal"); + m.find(".modal-title").html("Consentement RGPD"); + m.find(".modal-dialog").removeClass("modal-lg"); + untyped m.modal(); + ReactDOM.render(jsx('<$CookieConsentBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), + js.Browser.document.querySelector('#myModal .modal-body')); return false; } public function shop(place:Int, date:String) { - ReactDOM.render(jsx('<$Store date=$date place=$place/>'), js.Browser.document.querySelector('#shop')); + ReactDOM.render(jsx('<$Store date=$date place=$place/>'), js.Browser.document.querySelector('#shop')); } - - public function groupMap(lat:String,lng:String,address:String) { - ReactDOM.render(jsx('<$GroupMapRoot lat="$lat" lng="$lng" address="$address"/>'), js.Browser.document.querySelector('#map')); + + public function groupMap(lat:String, lng:String, address:String) { + ReactDOM.render(jsx('<$GroupMapRoot lat="$lat" lng="$lng" address="$address"/>'), js.Browser.document.querySelector('#map')); } /** * Helper to get values of a bunch of checked checkboxes * @param formSelector */ - public function getCheckboxesId(formSelector:String):Array{ + public function getCheckboxesId(formSelector:String):Array { var out = []; var checkboxes = js.Browser.document.querySelectorAll(formSelector + " input[type=checkbox]"); - for ( input in checkboxes ){ - var input : js.html.InputElement = cast input; - if ( input.checked ) out.push(input.value); + for (input in checkboxes) { + var input:js.html.InputElement = cast input; + if (input.checked) + out.push(input.value); } return out; } - #if plugins - public function getHostedPlugin(){ + public function getHostedPlugin() { return new hosted.js.App(); } #end @@ -223,40 +232,35 @@ class App { /** * set up a warning message when leaving the page */ - public function setWarningOnUnload(active:Bool, ?msg:String){ - if (active){ + public function setWarningOnUnload(active:Bool, ?msg:String) { + if (active) { js.Browser.window.addEventListener("beforeunload", warn); - }else{ + } else { js.Browser.window.removeEventListener("beforeunload", warn); } - } function warn(e:js.html.Event) { var msg = "Voulez vous vraiment quitter cette page ?"; - //js.Browser.window.confirm(msg); - untyped e.returnValue = msg; //Gecko + IE + // js.Browser.window.confirm(msg); + untyped e.returnValue = msg; // Gecko + IE e.preventDefault(); - return msg; //Gecko + Webkit, Safari, Chrome etc. + return msg; // Gecko + Webkit, Safari, Chrome etc. } /** * Anti Doubleclick with btn elements. * Can be bypassed by adding a .btn-noAntiDoubleClick class */ - public function antiDoubleClick(){ - - for( n in js.Browser.document.querySelectorAll(".btn:not(.btn-noAntiDoubleClick)") ){ - n.addEventListener("click",function(e:js.html.MouseEvent){ + public function antiDoubleClick() { + for (n in js.Browser.document.querySelectorAll(".btn:not(.btn-noAntiDoubleClick)")) { + n.addEventListener("click", function(e:js.html.MouseEvent) { var x = untyped e.target; x.classList.add("disabled"); - haxe.Timer.delay(function(){ + haxe.Timer.delay(function() { x.classList.remove("disabled"); - },1000); + }, 1000); }); } - } } - - diff --git a/js/react/user/CookieConsentBox.hx b/js/react/user/CookieConsentBox.hx new file mode 100644 index 0000000..b758eda --- /dev/null +++ b/js/react/user/CookieConsentBox.hx @@ -0,0 +1,101 @@ +package react.user; + +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +typedef CookieConsentBoxProps = { + redirectUrl:String, + message:String, + ?phoneRequired:Bool +} + +typedef CookieConsentBoxState = { + email:String, + password:String, + error:String +} + +/** + * CookieConsent Box + * @author fbarbut + */ +class CookieConsentBox extends react.ReactComponentOfPropsAndState { + public function new(props:CookieConsentBoxProps) { + if (props.redirectUrl == null || props.redirectUrl == "null") + props.redirectUrl = "/"; + if (props.message == "") + props.message = null; + super(props); + + this.state = {email: "", password: "", error: null}; + } + + function setError(err:String) { + this.setState(cast {error: err}); + } + + override public function render() { + return jsx('
+ <$Error error="${state.error}" /> + <$Message message="${props.message}" /> +
+

Personnaliser les préférences en matière de consentement

+

+ Nous n\'utilisons que des cookies nécessaires pour vous aider à naviguer efficacement et à exécuter certaines fonctionnalités. Les cookies nécessaires sont cruciaux pour les fonctions de base du site Web et celui-ci ne fonctionnera pas comme prévu sans eux. Ces cookies ne stockent aucune donnée personnellement identifiable. +

+ +

+ Refuser +   + Accepter +

+
+
'); + + } + + /** + * @doc https://facebook.github.io/react/docs/forms.html + */ + function onChange(e:js.html.Event) { + e.preventDefault(); + + var name:String = untyped e.target.name; + var value:String = untyped /*(e.target.value == "") ? null :*/ e.target.value; + Reflect.setField(state, name, value); + this.setState(this.state); + } + + public function submit(?e:js.html.Event) { + // lock button + var el:js.html.Element = null; + if (e != null) { + el = cast e.target; + el.classList.add("disabled"); + } + // var cookie = js.Browser.document.cookie; + var newCookie = "rgpd=true; SameSite=Lax"; + js.Browser.document.cookie = newCookie; + // js.Browser.window.location.href = props.redirectUrl; + js.Browser.window.location.href = '/'; + } + + public function deny(?e:js.html.Event) { + // lock button + var el:js.html.Element = null; + if (e != null) { + el = cast e.target; + el.classList.add("disabled"); + } + var newCookie = "rgpd=false; SameSite=Lax"; + js.Browser.document.cookie = newCookie; + // js.Browser.window.location.href = props.redirectUrl; + js.Browser.window.location.href = '/'; + } + + function onKeyPress(e:js.html.KeyboardEvent) { + if (e.key == "Enter") + submit(); + } +} diff --git a/lang/master/tpl/group/view.mtt b/lang/master/tpl/group/view.mtt index 6847d5f..be78f84 100644 --- a/lang/master/tpl/group/view.mtt +++ b/lang/master/tpl/group/view.mtt @@ -185,6 +185,17 @@ +::if user == null:: + +::end:: + + ::use 'footer.mtt':: ::end:: diff --git a/lang/master/tpl/user/cookieConsent.mtt b/lang/master/tpl/user/cookieConsent.mtt new file mode 100644 index 0000000..d341f94 --- /dev/null +++ b/lang/master/tpl/user/cookieConsent.mtt @@ -0,0 +1,14 @@ +::use 'design.mtt':: + +
+ +::if user != null:: + consent agreed +::else:: + +::end:: + +
+::end:: \ No newline at end of file diff --git a/package.json b/package.json index 8d47b2b..df77711 100644 --- a/package.json +++ b/package.json @@ -1 +1,51 @@ -{"name": "cagette-pei", "description": "le logiciel libre du circuit court", "license": "AGPL-3.0", "dependencies": {"bootstrap": "^3.3.4", "eonasdan-bootstrap-datetimepicker": "^4.7.14", "geolib": "^2.0.24", "haxe-modular": "^0.7.0", "jquery": "^2.2.4", "leaflet": "^1.3.1", "react": "^15.6.1", "react-bootstrap-typeahead": "1.4.2", "react-datetime": "^2.9.0", "react-dom": "^15.6.1", "react-leaflet": "^1.8.0", "react-places-autocomplete": "^6.0.2", "react-router": "^4.2.0", "react-router-dom": "^4.2.2"}, "devDependencies": {"babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babelify": "^7.3.0", "browserify": "^14.4.0", "cross-env": "^5.1.1", "node-sass": "^4.5.3", "npm-run-all": "^4.1.2", "uglify-js": "^3.1.9", "watchify": "^3.9.0"}, "scripts": {"build": "npm-run-all --parallel build:*", "build:js": "haxe cagetteJs.hxml", "build:sass": "node-sass www/css -o www/css", "watch:sass": "npm run build:sass -- --watch", "build2": "babel src -d lib", "libs:dev": "browserify js/libs/libs.js -o www/js/libs.js -d", "libs:prod": "cross-env NODE_ENV=production browserify js/libs/libs.js | uglifyjs -c -m > www/js/libs.js", "test": "haxe testsJS.hxml && node js/test/tests.js", "prepush": "npm test"}, "repository": {"type": "git", "url": "git+https://github.com/bablukid/cagette.git"}, "bugs": {"url": "https://github.com/bablukid/cagette/issues"}, "version": "1.14.2"} +{ + "name": "cagette-pei", + "description": "le logiciel libre du circuit court", + "license": "AGPL-3.0", + "dependencies": { + "bootstrap": "^3.3.4", + "eonasdan-bootstrap-datetimepicker": "^4.7.14", + "geolib": "^2.0.24", + "haxe-modular": "^0.7.0", + "jquery": "^2.2.4", + "leaflet": "^1.3.1", + "react": "^15.6.1", + "react-bootstrap-typeahead": "1.4.2", + "react-datetime": "^2.9.0", + "react-dom": "^15.6.1", + "react-leaflet": "^1.8.0", + "react-places-autocomplete": "^6.0.2", + "react-router": "^4.2.0", + "react-router-dom": "^4.2.2" + }, + "devDependencies": { + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babelify": "^7.3.0", + "browserify": "^14.4.0", + "cross-env": "^5.1.1", + "node-sass": "^4.5.3", + "npm-run-all": "^4.1.2", + "uglify-js": "^3.1.9", + "watchify": "^3.9.0" + }, + "scripts": { + "build": "npm-run-all --parallel build:*", + "build:js": "haxe cagetteJs.hxml", + "build:sass": "node-sass www/css -o www/css", + "watch:sass": "npm run build:sass -- --watch", + "build2": "babel src -d lib", + "libs:dev": "browserify js/libs/libs.js -o www/js/libs.js -d", + "libs:prod": "cross-env NODE_ENV=production browserify js/libs/libs.js | uglifyjs -c -m > www/js/libs.js", + "test": "haxe testsJS.hxml && node js/test/tests.js", + "prepush": "npm test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bablukid/cagette.git" + }, + "bugs": { + "url": "https://github.com/bablukid/cagette/issues" + }, + "version": "1.14.2" +} diff --git a/src/controller/User.hx b/src/controller/User.hx index 3c2f3af..eb8ea3b 100755 --- a/src/controller/User.hx +++ b/src/controller/User.hx @@ -30,6 +30,21 @@ class User extends Controller { } } + @tpl("user/cookieConsent.mtt") + function doCookieConsent() { + App.log("Cookie Consent called!"); + // if (App.current.user != null) { + // throw Redirect('/'); + // } + + // if its needed to redirect after login + if (app.params.exists("redirect")) { + view.redirect = app.params.exists("redirect"); + } else { + view.redirect = "/"; + } + } + /** * Choose which group to connect to. */