Create rent, display rent offers

This commit is contained in:
Adrian Woźniak 2023-10-11 17:22:53 +02:00
parent 9329c57970
commit bd61bee720
36 changed files with 819 additions and 463 deletions

623
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,43 @@
(()=>{var h=Object.create;var n=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var p=Object.getPrototypeOf,f=Object.prototype.hasOwnProperty;var g=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,s)=>(typeof require<"u"?require:t)[s]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var S=(e,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of u(t))!f.call(e,r)&&r!==s&&n(e,r,{get:()=>t[r],enumerable:!(o=m(t,r))||o.enumerable});return e};var y=(e,t,s)=>(s=e!=null?h(p(e)):{},S(t||!e||!e.__esModule?n(s,"default",{value:e,enumerable:!0}):s,e));customElements.define("oswilno-price",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){let e=this.shadowRoot,t=parseInt(this.getAttribute("price"));isNaN(t)&&(t=0);let s=parseInt(this.getAttribute("multiplier")),o=t,r=0;isNaN(s)||(o=Math.floor(t/s),r=t%s);let l=this.getAttribute("currency")||"PLN";e.innerHTML=`<style>:host{display:block;}</style><div>${o}.${r>=10?r:r+"0"} ${l}</div>`}});var c=e=>{let t="";for(let o=0;o<document.styleSheets.length;o++){let r=document.styleSheets[o];for(let l=0;l<r.rules.length;l++)t+=r.rules[l].cssText}let s=new CSSStyleSheet;s.replaceSync(t),e.adoptedStyleSheets=[s]};customElements.define("oswilno-error",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
(()=>{var h=Object.create;var l=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var u=Object.getPrototypeOf,w=Object.prototype.hasOwnProperty;var f=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,s)=>(typeof require<"u"?require:t)[s]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var y=(e,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of m(t))!w.call(e,r)&&r!==s&&l(e,r,{get:()=>t[r],enumerable:!(o=p(t,r))||o.enumerable});return e};var g=(e,t,s)=>(s=e!=null?h(u(e)):{},y(t||!e||!e.__esModule?l(s,"default",{value:e,enumerable:!0}):s,e));customElements.define("oswilno-price",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){let e=this.shadowRoot,t=parseInt(this.getAttribute("price"));isNaN(t)&&(t=0);let s=parseInt(this.getAttribute("multiplier")),o=t,r=0;isNaN(s)||(o=Math.floor(t/s),r=t%s);let n=this.getAttribute("currency")||"PLN";e.innerHTML=`<style>:host{display:block;}</style><div>${o}.${r>=10?r:r+"0"} ${n}</div>`}});var i=e=>{let t="";for(let o=0;o<document.styleSheets.length;o++){let r=document.styleSheets[o];for(let n=0;n<r.rules.length;n++)t+=r.rules[n].cssText}let s=new CSSStyleSheet;s.replaceSync(t),e.adoptedStyleSheets=[s]};customElements.define("oswilno-error",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
<style>:host{display:block;}</style>
<div class="flex bg-red-100 rounded-lg p-4 mb-4 text-sm text-red-700">
<slot></slot>
<div>
`,c(e)}});customElements.define("oswilno-parking-space",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
`,i(e)}});customElements.define("oswilno-parking-space-rents",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
<style>
:host {
display: block;
width: 100%;
}
#container {
display: flex;
justify-content: space-between;
width: 100%;
}
</style>
<div id="container">
<slot></slot>
</div>
`}});customElements.define("oswilno-parking-space-rent",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
<style>
:host { display: block; width: 100%; }
</style>
<div class="w-[48%] p-4 bg-white border border-white-200 rounded-lg shadow sm:p-8 dark:bg-white-800 dark:border-white-700 mt-6">
<slot></slot>
</div>
`}});customElements.define("oswilno-parking-space",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
<style>
:host { display: block; width: 100%; }
</style>
<div>
<slot></slot>
</div>
`}});import("https://unpkg.com/htmx.org@1.9.4/dist/htmx.min.js");var a="Authorization",w="ACX-Authorization",i="ACX-Refresh",d=document.body;d.addEventListener("htmx:beforeOnLoad",function(e){let t=e.detail,s=t.xhr,o=s.status,r=t.successful;if(o===200){let l=s.getResponseHeader(a);l&&(console.log(s),localStorage.setItem("jwt",l.replace(/^Bearer /i,""))),s.getResponseHeader(i)&&localStorage.setItem("refresh",l.replace(/^Bearer /i,""))}else o===401&&localStorage.removeItem("jwt");(o===422||o===400)&&(t.shouldSwap=!0,t.isError=!1)});d.addEventListener("htmx:configRequest",function(e){localStorage.getItem("jwt")&&(e.detail.headers[w]="Bearer "+(localStorage.getItem("jwt")||""),e.detail.headers[a]="Bearer "+(localStorage.getItem("jwt")||""),e.detail.headers[i]=localStorage.getItem("refresh")||"")});})();
`}});customElements.define("oswilno-parking-space-location",class extends HTMLElement{constructor(){super();let e=this.attachShadow({mode:"open"});e.innerHTML=`
<style>
:host { display: block; width: 100%; }
</style>
<div>
<slot></slot>
</div>
`}});import("https://unpkg.com/htmx.org@1.9.4/dist/htmx.min.js");var a="Authorization",S="ACX-Authorization",c="ACX-Refresh",d=document.body;d.addEventListener("htmx:beforeOnLoad",function(e){let t=e.detail,s=t.xhr,o=s.status,r=t.successful;if(o===200){let n=s.getResponseHeader(a);n&&(console.log(s),localStorage.setItem("jwt",n.replace(/^Bearer /i,""))),s.getResponseHeader(c)&&localStorage.setItem("refresh",n.replace(/^Bearer /i,""))}else o===401&&localStorage.removeItem("jwt");(o===422||o===400)&&(t.shouldSwap=!0,t.isError=!1)});d.addEventListener("htmx:configRequest",function(e){localStorage.getItem("jwt")&&(e.detail.headers[S]="Bearer "+(localStorage.getItem("jwt")||""),e.detail.headers[a]="Bearer "+(localStorage.getItem("jwt")||""),e.detail.headers[c]=localStorage.getItem("refresh")||"")});})();
//# sourceMappingURL=build.js.map

View File

@ -1,7 +1,7 @@
{
"version": 3,
"sources": ["../crates/web-assets/assets/elements/oswilno-price.js", "../crates/web-assets/assets/css.js", "../crates/web-assets/assets/elements/oswilno-error.js", "../crates/web-assets/assets/elements/oswilno-parking-space.js", "../crates/web-assets/assets/app.js"],
"sourcesContent": ["customElements.define('oswilno-price', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.attachShadow({ mode: 'open' });\n\t}\n\tconnectedCallback() {\n\t\tlet shadow = this.shadowRoot;\n\t\tlet price = parseInt(this.getAttribute('price'));\n\t\tif (isNaN(price)) price = 0;\n\t\tconst multiplier = parseInt(this.getAttribute('multiplier'));\n\t\tlet major = price;\n\t\tlet minor = 0;\n\t\tif (!isNaN(multiplier)) {\n\t\t\tmajor = Math.floor(price / multiplier);\n\t\t\tminor = price % multiplier;\n\t\t}\n\t\tconst currency = this.getAttribute('currency') || 'PLN';\n\t\tshadow.innerHTML = `<style>:host{display:block;}</style><div>${major}.${minor >= 10 ? minor : minor + '0'} ${ currency }</div>`;\n\t}\n});\n", "export const copyCss = (shadow) => {\n\tlet css = '';\n\tfor (let i = 0; i < document.styleSheets.length; i++) {\n\t\tconst styleSheet = document.styleSheets[i];\n\t\tfor (let j = 0; j < styleSheet.rules.length; j++) {\n\t\t\tcss += styleSheet.rules[j].cssText;\t\n\t\t}\n\t}\n\tconst sheet = new CSSStyleSheet();\n\tsheet.replaceSync(css);\n\tshadow.adoptedStyleSheets = [sheet];\n};\n", "import { copyCss } from \"../css.js\";\n\ncustomElements.define('oswilno-error', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>:host{display:block;}</style>\n\t\t\t<div class=\"flex bg-red-100 rounded-lg p-4 mb-4 text-sm text-red-700\">\n\t\t\t\t<slot></slot>\n\t\t\t<div>\n\t\t`;\n\t\tcopyCss(shadow);\n\t}\n});\n", "customElements.define('oswilno-parking-space', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>\n\t\t\t:host { display: block; width: 100%; }\n\t\t\t</style>\n\t\t\t<div>\n\t\t\t<slot></slot>\n\t\t\t</div>\n\t\t`;\n\t}\n});\n", "import './elements/oswilno-price.js';\nimport './elements/oswilno-error.js';\nimport './elements/oswilno-parking-space.js';\nimport(\"https://unpkg.com/htmx.org@1.9.4/dist/htmx.min.js\");\n\nconst READ_AUTH_HEADER = 'Authorization';\nconst AUTH_HEADER = 'ACX-Authorization';\nconst REFRESH_HEADER = 'ACX-Refresh';\nconst body = document.body;\nbody.addEventListener('htmx:beforeOnLoad', function (evt) {\n\tconst detail = evt.detail;\n\tconst xhr = detail.xhr;\n\tconst status = xhr.status;\n\tconst successful = detail.successful;\n\n\tif (status === 200) {\n\t\tconst bearer = xhr.getResponseHeader(READ_AUTH_HEADER);\n\t\tif (bearer) {\n\t\t\tconsole.log(xhr);\n\t\t\tlocalStorage.setItem('jwt', bearer.replace(/^Bearer /i, ''));\n\t\t}\n\t\tconst refresh = xhr.getResponseHeader(REFRESH_HEADER);\n\t\tif (refresh) {\n\t\t\tlocalStorage.setItem('refresh', bearer.replace(/^Bearer /i, ''));\n\t\t}\n\t} else if (status === 401) {\n\t\tlocalStorage.removeItem('jwt');\n\t}\n\tif (status === 422 || status === 400) {\n\t\tdetail.shouldSwap = true;\n\t\tdetail.isError = false;\n\t}\n});\nbody.addEventListener('htmx:configRequest', function (evt) {\n\tif (!localStorage.getItem('jwt')) {\n\t\treturn;\n\t}\n\tevt.detail.headers[AUTH_HEADER] = 'Bearer ' + (localStorage.getItem('jwt') || '');\n\tevt.detail.headers[READ_AUTH_HEADER] = 'Bearer ' + (localStorage.getItem('jwt') || '');\n\tevt.detail.headers[REFRESH_HEADER] = (localStorage.getItem('refresh') || '');\n});\n\n"],
"mappings": "0sBAAA,eAAe,OAAO,gBAAiB,cAAc,WAAY,CAChE,aAAc,CACb,MAAM,EACN,KAAK,aAAa,CAAE,KAAM,MAAO,CAAC,CACnC,CACA,mBAAoB,CACnB,IAAIA,EAAS,KAAK,WACdC,EAAQ,SAAS,KAAK,aAAa,OAAO,CAAC,EAC3C,MAAMA,CAAK,IAAGA,EAAQ,GAC1B,IAAMC,EAAa,SAAS,KAAK,aAAa,YAAY,CAAC,EACvDC,EAAQF,EACRG,EAAQ,EACP,MAAMF,CAAU,IACpBC,EAAQ,KAAK,MAAMF,EAAQC,CAAU,EACrCE,EAAQH,EAAQC,GAEjB,IAAMG,EAAW,KAAK,aAAa,UAAU,GAAK,MAClDL,EAAO,UAAY,4CAA4CG,CAAK,IAAIC,GAAS,GAAKA,EAAQA,EAAQ,GAAG,IAAKC,CAAS,QACxH,CACD,CAAC,ECnBM,IAAMC,EAAWC,GAAW,CAClC,IAAIC,EAAM,GACV,QAASC,EAAI,EAAGA,EAAI,SAAS,YAAY,OAAQA,IAAK,CACrD,IAAMC,EAAa,SAAS,YAAYD,CAAC,EACzC,QAASE,EAAI,EAAGA,EAAID,EAAW,MAAM,OAAQC,IAC5CH,GAAOE,EAAW,MAAMC,CAAC,EAAE,OAE7B,CACA,IAAMC,EAAQ,IAAI,cAClBA,EAAM,YAAYJ,CAAG,EACrBD,EAAO,mBAAqB,CAACK,CAAK,CACnC,ECTA,eAAe,OAAO,gBAAiB,cAAc,WAAY,CAChE,aAAc,CACb,MAAM,EACN,IAAMC,EAAS,KAAK,aAAa,CAAE,KAAM,MAAO,CAAC,EACjDA,EAAO,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA,IAMnBC,EAAQD,CAAM,CACf,CACD,CAAC,ECdD,eAAe,OAAO,wBAAyB,cAAc,WAAY,CACxE,aAAc,CACb,MAAM,EACN,IAAME,EAAS,KAAK,aAAa,CAAE,KAAM,MAAO,CAAC,EACjDA,EAAO,UAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAQpB,CACD,CAAC,ECVD,OAAO,mDAAmD,EAE1D,IAAMC,EAAmB,gBACnBC,EAAc,oBACdC,EAAiB,cACjBC,EAAO,SAAS,KACtBA,EAAK,iBAAiB,oBAAqB,SAAUC,EAAK,CACzD,IAAMC,EAASD,EAAI,OACbE,EAAMD,EAAO,IACbE,EAASD,EAAI,OACbE,EAAaH,EAAO,WAE1B,GAAIE,IAAW,IAAK,CACnB,IAAME,EAASH,EAAI,kBAAkBN,CAAgB,EACjDS,IACH,QAAQ,IAAIH,CAAG,EACf,aAAa,QAAQ,MAAOG,EAAO,QAAQ,YAAa,EAAE,CAAC,GAE5CH,EAAI,kBAAkBJ,CAAc,GAEnD,aAAa,QAAQ,UAAWO,EAAO,QAAQ,YAAa,EAAE,CAAC,CAEjE,MAAWF,IAAW,KACrB,aAAa,WAAW,KAAK,GAE1BA,IAAW,KAAOA,IAAW,OAChCF,EAAO,WAAa,GACpBA,EAAO,QAAU,GAEnB,CAAC,EACDF,EAAK,iBAAiB,qBAAsB,SAAUC,EAAK,CACrD,aAAa,QAAQ,KAAK,IAG/BA,EAAI,OAAO,QAAQH,CAAW,EAAI,WAAa,aAAa,QAAQ,KAAK,GAAK,IAC9EG,EAAI,OAAO,QAAQJ,CAAgB,EAAI,WAAa,aAAa,QAAQ,KAAK,GAAK,IACnFI,EAAI,OAAO,QAAQF,CAAc,EAAK,aAAa,QAAQ,SAAS,GAAK,GAC1E,CAAC",
"names": ["shadow", "price", "multiplier", "major", "minor", "currency", "copyCss", "shadow", "css", "i", "styleSheet", "j", "sheet", "shadow", "copyCss", "shadow", "READ_AUTH_HEADER", "AUTH_HEADER", "REFRESH_HEADER", "body", "evt", "detail", "xhr", "status", "successful", "bearer"]
"sources": ["../crates/web-assets/assets/elements/oswilno-price.js", "../crates/web-assets/assets/css.js", "../crates/web-assets/assets/elements/oswilno-error.js", "../crates/web-assets/assets/elements/oswilno-parking-space-rents.js", "../crates/web-assets/assets/elements/oswilno-parking-space-rent.js", "../crates/web-assets/assets/elements/oswilno-parking-space.js", "../crates/web-assets/assets/elements/oswilno-parking-space-location.js", "../crates/web-assets/assets/app.js"],
"sourcesContent": ["customElements.define('oswilno-price', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.attachShadow({ mode: 'open' });\n\t}\n\tconnectedCallback() {\n\t\tlet shadow = this.shadowRoot;\n\t\tlet price = parseInt(this.getAttribute('price'));\n\t\tif (isNaN(price)) price = 0;\n\t\tconst multiplier = parseInt(this.getAttribute('multiplier'));\n\t\tlet major = price;\n\t\tlet minor = 0;\n\t\tif (!isNaN(multiplier)) {\n\t\t\tmajor = Math.floor(price / multiplier);\n\t\t\tminor = price % multiplier;\n\t\t}\n\t\tconst currency = this.getAttribute('currency') || 'PLN';\n\t\tshadow.innerHTML = `<style>:host{display:block;}</style><div>${major}.${minor >= 10 ? minor : minor + '0'} ${ currency }</div>`;\n\t}\n});\n", "export const copyCss = (shadow) => {\n\tlet css = '';\n\tfor (let i = 0; i < document.styleSheets.length; i++) {\n\t\tconst styleSheet = document.styleSheets[i];\n\t\tfor (let j = 0; j < styleSheet.rules.length; j++) {\n\t\t\tcss += styleSheet.rules[j].cssText;\t\n\t\t}\n\t}\n\tconst sheet = new CSSStyleSheet();\n\tsheet.replaceSync(css);\n\tshadow.adoptedStyleSheets = [sheet];\n};\n", "import { copyCss } from \"../css.js\";\n\ncustomElements.define('oswilno-error', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>:host{display:block;}</style>\n\t\t\t<div class=\"flex bg-red-100 rounded-lg p-4 mb-4 text-sm text-red-700\">\n\t\t\t\t<slot></slot>\n\t\t\t<div>\n\t\t`;\n\t\tcopyCss(shadow);\n\t}\n});\n", "customElements.define('oswilno-parking-space-rents', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>\n\t\t\t:host {\n display: block;\n width: 100%;\n }\n #container {\n display: flex;\n justify-content: space-between;\n width: 100%;\n }\n\t\t\t</style>\n\t\t\t<div id=\"container\">\n <slot></slot>\n\t\t\t</div>\n\t\t`;\n\t}\n});\n\n", "customElements.define('oswilno-parking-space-rent', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>\n\t\t\t:host { display: block; width: 100%; }\n\t\t\t</style>\n\t\t\t<div class=\"w-[48%] p-4 bg-white border border-white-200 rounded-lg shadow sm:p-8 dark:bg-white-800 dark:border-white-700 mt-6\">\n <slot></slot>\n\t\t\t</div>\n\t\t`;\n\t}\n});\n", "customElements.define('oswilno-parking-space', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>\n\t\t\t:host { display: block; width: 100%; }\n\t\t\t</style>\n\t\t\t<div>\n <slot></slot>\n\t\t\t</div>\n\t\t`;\n\t}\n});\n", "customElements.define('oswilno-parking-space-location', class extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tconst shadow = this.attachShadow({ mode: 'open' });\n\t\tshadow.innerHTML = `\n\t\t\t<style>\n\t\t\t:host { display: block; width: 100%; }\n\t\t\t</style>\n\t\t\t<div>\n <slot></slot>\n\t\t\t</div>\n\t\t`;\n\t}\n});\n", "import './elements/oswilno-price.js';\nimport './elements/oswilno-error.js';\nimport './elements/oswilno-parking-space-rents.js';\nimport './elements/oswilno-parking-space-rent.js';\nimport './elements/oswilno-parking-space.js';\nimport './elements/oswilno-parking-space-location.js';\n\nimport(\"https://unpkg.com/htmx.org@1.9.4/dist/htmx.min.js\");\n\nconst READ_AUTH_HEADER = 'Authorization';\nconst AUTH_HEADER = 'ACX-Authorization';\nconst REFRESH_HEADER = 'ACX-Refresh';\nconst body = document.body;\nbody.addEventListener('htmx:beforeOnLoad', function (evt) {\n\tconst detail = evt.detail;\n\tconst xhr = detail.xhr;\n\tconst status = xhr.status;\n\tconst successful = detail.successful;\n\n\tif (status === 200) {\n\t\tconst bearer = xhr.getResponseHeader(READ_AUTH_HEADER);\n\t\tif (bearer) {\n\t\t\tconsole.log(xhr);\n\t\t\tlocalStorage.setItem('jwt', bearer.replace(/^Bearer /i, ''));\n\t\t}\n\t\tconst refresh = xhr.getResponseHeader(REFRESH_HEADER);\n\t\tif (refresh) {\n\t\t\tlocalStorage.setItem('refresh', bearer.replace(/^Bearer /i, ''));\n\t\t}\n\t} else if (status === 401) {\n\t\tlocalStorage.removeItem('jwt');\n\t}\n\tif (status === 422 || status === 400) {\n\t\tdetail.shouldSwap = true;\n\t\tdetail.isError = false;\n\t}\n});\nbody.addEventListener('htmx:configRequest', function (evt) {\n\tif (!localStorage.getItem('jwt')) {\n\t\treturn;\n\t}\n\tevt.detail.headers[AUTH_HEADER] = 'Bearer ' + (localStorage.getItem('jwt') || '');\n\tevt.detail.headers[READ_AUTH_HEADER] = 'Bearer ' + (localStorage.getItem('jwt') || '');\n\tevt.detail.headers[REFRESH_HEADER] = (localStorage.getItem('refresh') || '');\n});\n\n"],
"mappings": "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",
"names": ["shadow", "price", "multiplier", "major", "minor", "currency", "copyCss", "shadow", "css", "i", "styleSheet", "j", "sheet", "shadow", "copyCss", "shadow", "shadow", "shadow", "shadow", "READ_AUTH_HEADER", "AUTH_HEADER", "REFRESH_HEADER", "body", "evt", "detail", "xhr", "status", "successful", "bearer"]
}

View File

@ -905,6 +905,10 @@ select {
width: 2rem;
}
.w-\[48\%\] {
width: 48%;
}
.w-full {
width: 100%;
}

View File

@ -1,6 +1,7 @@
use crate::m20220101_000001_create_table::Account;
use sea_orm_migration::prelude::*;
use crate::m20220101_000001_create_table::Account;
#[derive(DeriveMigrationName)]
pub struct Migration;

View File

@ -1,8 +1,7 @@
use sea_orm_migration::prelude::*;
use crate::m20220101_000001_create_table::Account;
use crate::m20230726_135630_parking_spaces::ParkingSpace;
use crate::m20230726_135630_parking_spaces::ParkingSpaceRent;
use crate::m20230726_135630_parking_spaces::{ParkingSpace, ParkingSpaceRent};
#[derive(DeriveMigrationName)]
pub struct Migration;

View File

@ -5,8 +5,8 @@ edition = "2021"
[dependencies]
# actix-admin = "0.5.0"
# actix-admin = { git = "https://github.com/Eraden/actix-admin.git", features = ['enable-tracing'] }
actix-admin = { git = "https://code.ita-prog.pl/Tsumanu/actix-admin.git", features = ['enable-tracing'] }
actix-admin = { git = "https://github.com/mgugger/actix-admin.git" }
# actix-admin = { git = "https://code.ita-prog.pl/Tsumanu/actix-admin.git", features = ['enable-tracing'] }
actix-web = "4.3.1"
actix-web-grants = "3.0.2"
askama = "0.12.0"

View File

@ -17,6 +17,7 @@ fn create_actix_admin_builder() -> ActixAdminBuilder {
logout_link: None,
file_upload_directory: "./file_uploads",
navbar_title: "oswilno - admin",
user_tenant_ref: None,
};
let mut admin_builder = ActixAdminBuilder::new(configuration);

View File

@ -7,9 +7,10 @@ edition = "2021"
actix = "0.13.0"
# actix-admin = "0.5.0"
# actix-admin = { git = "https://github.com/Eraden/actix-admin.git", features = ['enable-tracing'] }
actix-admin = { git = "https://code.ita-prog.pl/Tsumanu/actix-admin.git", features = ['enable-tracing'] }
actix-admin = { git = "https://github.com/mgugger/actix-admin.git" }
# actix-admin = { git = "https://code.ita-prog.pl/Tsumanu/actix-admin.git", features = ['enable-tracing'] }
actix-rt = { version = "2.8.0", features = [] }
chrono = "0.4.26"
chrono = { version = "0.4.26", features = ["serde"] }
oswilno-actix-admin = { path = "../oswilno-actix-admin" }
regex = "1.9.1"
sea-orm = { version = "0.12", features = ["postgres-array", "runtime-actix-rustls", "sqlx-postgres", "macros", "sqlx"] }

View File

@ -1,11 +1,12 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use super::sea_orm_active_enums::Userrole;
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;
#[allow(unused_imports)]
use sea_orm::Iterable;
use super::sea_orm_active_enums::Userrole;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;

View File

@ -1,11 +1,12 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use super::sea_orm_active_enums::ImageState;
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;
#[allow(unused_imports)]
use sea_orm::Iterable;
use super::sea_orm_active_enums::ImageState;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;

View File

@ -1,4 +1,4 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
pub mod prelude;
@ -9,5 +9,4 @@ pub mod parking_space_rents;
pub mod parking_spaces;
pub mod rent_requests;
pub mod sea_orm_active_enums;
pub use actix_admin;
pub use sea_orm;
pub use {::chrono, actix_admin, sea_orm};

View File

@ -1,4 +1,4 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;

View File

@ -1,4 +1,4 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;

View File

@ -1,11 +1,12 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use super::sea_orm_active_enums::ParkingSpaceState;
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;
#[allow(unused_imports)]
use sea_orm::Iterable;
use super::sea_orm_active_enums::ParkingSpaceState;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;

View File

@ -1,4 +1,4 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
pub use super::accounts::Entity as Accounts;
pub use super::images::Entity as Images;

View File

@ -1,4 +1,4 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;

View File

@ -1,4 +1,4 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.2
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3
use actix_admin::prelude::*;
use sea_orm::entity::prelude::*;

View File

@ -1,15 +1,20 @@
#![feature(result_option_inspect)]
use std::collections::BTreeMap;
use std::rc::Rc;
use std::sync::Arc;
use actix_web::http::header::ContentType;
use actix_web::web::{scope, Data, Form, Path, ServiceConfig};
use actix_web::{get, post, put, HttpRequest, HttpResponse};
use askama_actix::Template;
use autometrics::autometrics;
use oswilno_contract::parking_space_locations::Model as ParkingSpaceLocation;
use oswilno_contract::parking_space_rents;
use oswilno_contract::parking_space_rents::Model as ParkingSpaceRent;
use oswilno_contract::parking_spaces;
use oswilno_contract::parking_spaces::Model as ParkingSpace;
use oswilno_contract::sea_orm_active_enums::ParkingSpaceState;
use oswilno_contract::{accounts, parking_space_locations};
use oswilno_contract::{
accounts, chrono, parking_space_locations, parking_space_rents, parking_spaces,
};
use oswilno_session::{Authenticated, MaybeAuthenticated};
use oswilno_view::{
filters, is_partial, Blank, Errors, HelperContext, Layout, Main, MainOpts, SearchOpts,
@ -19,9 +24,6 @@ use sea_orm::prelude::*;
use sea_orm::ActiveValue::{NotSet, Set};
use sea_orm::QueryOrder;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::rc::Rc;
use std::sync::Arc;
pub async fn init(db: Arc<sea_orm::DatabaseConnection>) {
ensure_locations(db).await;
@ -490,48 +492,38 @@ async fn parking_space_rent_form(
) -> HttpResponse {
let parking_space_id = parking_space_id.into_inner();
let db = db.into_inner();
let _account_id = session.as_ref().map(|s| s.account_id());
let parking_space = {
use ::oswilno_contract::parking_spaces::{Column, Entity};
let res = Entity::find()
.filter(Column::Id.eq(parking_space_id))
let account_id = session.as_ref().map(|s| s.account_id());
let q = parking_spaces::Entity::find().filter(parking_spaces::Column::Id.eq(parking_space_id));
let Ok(Some(parking_space)) = match account_id {
Some(id) => q.filter(parking_spaces::Column::AccountId.eq(id)),
None => q,
}
.one(&*db)
.await;
match res {
Ok(Some(entity)) => entity,
error => {
tracing::debug!("Parking space does not exists: {error:?}");
return HttpResponse::NotFound()
.body("Invalid request, parking space does not exists");
}
}
.await
.inspect_err(|error| tracing::debug!("Parking space does not exists: {error:?}")) else {
return HttpResponse::NotFound().body("Invalid request, parking space does not exists");
};
let location = {
use ::oswilno_contract::parking_space_locations::{Column, Entity};
let Some(location_id) = parking_space.location_id else {
tracing::debug!("Parking space does not have location id");
return HttpResponse::NotFound()
.body("Invalid request, parking space does not have location");
};
let res = Entity::find()
.filter(Column::Id.eq(location_id))
let Ok(Some(location)) = parking_space_locations::Entity::find()
.filter(parking_space_locations::Column::Id.eq(location_id))
.one(&*db)
.await;
match res {
Ok(Some(entity)) => entity,
error => {
tracing::debug!("Parking space location does not exists: {error:?}");
.await
.inspect_err(|error| tracing::debug!("Parking space location does not exists: {error:?}"))
else {
return HttpResponse::NotFound()
.body("Invalid request, parking space location does not exists");
}
}
};
let html = render_rent_form(
ParkingSpaceRentForm::default(),
&req,
parking_space,
location,
session,
session.into_option().map(Into::into),
hcx,
);
HttpResponse::Ok()
@ -547,20 +539,223 @@ async fn parking_space_rent_form(
.body(html)
}
#[get("/{parking_space_id}/parking-space-rents/edit/{id}")]
async fn parking_space_rent_edit(_id: Path<i32>) -> HttpResponse {
todo!()
async fn parking_space_rent_edit(
req: HttpRequest,
id: Path<i32>,
parking_space_id: Path<i32>,
session: Authenticated,
db: Data<sea_orm::DatabaseConnection>,
hcx: HelperContext,
) -> HttpResponse {
let parking_space_id = parking_space_id.into_inner();
let id = id.into_inner();
let db = db.into_inner();
let account_id = session.account_id();
let session = session.into();
let Ok(Some(parking_space)) = parking_spaces::Entity::find()
.filter(parking_spaces::Column::Id.eq(parking_space_id))
.filter(parking_spaces::Column::AccountId.eq(account_id))
.one(&*db)
.await
.inspect_err(|error| tracing::debug!("Parking space does not exists: {error:?}"))
else {
return HttpResponse::NotFound().body("Invalid request, parking space does not exists");
};
let Ok(Some(rent)) = parking_space_rents::Entity::find()
.filter(parking_space_rents::Column::Id.eq(id))
.filter(parking_space_rents::Column::ParkingSpaceId.eq(parking_space_id))
.one(&*db)
.await
.inspect_err(|e| tracing::warn!("Parking space rent {id} not found: {e}"))
else {
return HttpResponse::NotFound()
.body("Invalid request, parking space rent does not exists");
};
let Some(location_id) = parking_space.location_id else {
tracing::debug!("Parking space does not have location id");
return HttpResponse::NotFound()
.body("Invalid request, parking space does not have location");
};
let Ok(Some(location)) = parking_space_locations::Entity::find()
.filter(parking_space_locations::Column::Id.eq(location_id))
.one(&*db)
.await
.inspect_err(|error| tracing::debug!("Parking space location does not exists: {error:?}"))
else {
return HttpResponse::NotFound()
.body("Invalid request, parking space location does not exists");
};
let html = render_rent_form(
ParkingSpaceRentForm {
price: None,
id: Some(id),
price_f: Some(rent.price as f64 / 100.0),
},
&req,
parking_space,
location,
Some(session),
hcx,
);
HttpResponse::Ok()
.append_header((
"HX-Redirect",
format!("/parking-spaces/{parking_space_id}/parking-space-rents/{id}/edit",).as_str(),
))
.append_header(("HX-Retarget", "main"))
.body(html)
}
#[post("/{parking_space_id}/parking-space-rents/create")]
async fn parking_space_rent_create(form: Form<ParkingSpaceRentForm>) -> HttpResponse {
async fn parking_space_rent_create(
req: HttpRequest,
form: Form<ParkingSpaceRentForm>,
parking_space_id: Path<i32>,
db: Data<sea_orm::DatabaseConnection>,
session: Authenticated,
hcx: HelperContext,
) -> HttpResponse {
let account_id = session.account_id();
let session = session.into();
let db = db.into_inner();
let parking_space_id = parking_space_id.into_inner();
let mut form = form.into_inner();
form.price = form.price_f.map(|n| n * 100.0).map(|n| n as i32);
todo!()
let Ok(Some(parking_space)) = parking_spaces::Entity::find()
.filter(parking_spaces::Column::Id.eq(parking_space_id))
.filter(parking_spaces::Column::AccountId.eq(account_id))
.one(&*db)
.await
else {
return HttpResponse::InternalServerError()
.body("Invalid request, parking space does not exists");
};
let Some(location_id) = parking_space.location_id else {
tracing::debug!("Parking space does not have location id");
return HttpResponse::NotFound()
.body("Invalid request, parking space does not have location");
};
let Ok(Some(location)) = parking_space_locations::Entity::find()
.filter(parking_space_locations::Column::Id.eq(location_id))
.one(&*db)
.await
.inspect_err(|error| tracing::debug!("Parking space location does not exists: {error:?}"))
else {
return HttpResponse::NotFound()
.body("Invalid request, parking space location does not exists");
};
let Some(price) = form.price else {
return HttpResponse::BadRequest().body(render_rent_form(
form,
&req,
parking_space,
location,
Some(session),
hcx.clone(),
));
};
let entity = parking_space_rents::ActiveModel {
price: Set(price),
parking_space_id: Set(parking_space.id),
..Default::default()
};
let Ok(_rent) = entity.save(&*db).await.inspect_err(|e| {
tracing::error!(
"Failed to create new rent for parking space {}: {e}",
parking_space.id
)
}) else {
return HttpResponse::BadRequest().body(render_rent_form(
form,
&req,
parking_space,
location,
Some(session),
hcx.clone(),
));
};
HttpResponse::SeeOther()
.append_header(("HX-Redirect", "/parking-spaces/all"))
.append_header(("Location", "/parking-spaces/all"))
.body("")
}
#[put("/{parking_space_id}/parking-space-rents/update")]
async fn parking_space_rent_update(form: Form<ParkingSpaceRentForm>) -> HttpResponse {
async fn parking_space_rent_update(
req: HttpRequest,
form: Form<ParkingSpaceRentForm>,
parking_space_id: Path<i32>,
db: Data<sea_orm::DatabaseConnection>,
session: Authenticated,
hcx: HelperContext,
) -> HttpResponse {
let account_id = session.account_id();
let session = session.into();
let db = db.into_inner();
let parking_space_id = parking_space_id.into_inner();
let mut form = form.into_inner();
form.price = form.price_f.map(|n| n * 100.0).map(|n| n as i32);
todo!()
let Some(rent_id) = form.id else {
return HttpResponse::InternalServerError().body("Requires rent ID to update");
};
let Ok(Some(parking_space)) = parking_spaces::Entity::find()
.filter(parking_spaces::Column::Id.eq(parking_space_id))
.filter(parking_spaces::Column::AccountId.eq(account_id))
.one(&*db)
.await
else {
return HttpResponse::InternalServerError()
.body("Invalid request, parking space does not exists");
};
let Some(location_id) = parking_space.location_id else {
tracing::debug!("Parking space does not have location id");
return HttpResponse::NotFound()
.body("Invalid request, parking space does not have location");
};
let Ok(Some(location)) = parking_space_locations::Entity::find()
.filter(parking_space_locations::Column::Id.eq(location_id))
.one(&*db)
.await
.inspect_err(|error| tracing::debug!("Parking space location does not exists: {error:?}"))
else {
return HttpResponse::NotFound()
.body("Invalid request, parking space location does not exists");
};
let Some(price) = form.price else {
return HttpResponse::BadRequest().body(render_rent_form(
form,
&req,
parking_space,
location,
Some(session),
hcx.clone(),
));
};
let entity = parking_space_rents::ActiveModel {
id: Set(rent_id),
price: Set(price),
parking_space_id: Set(parking_space.id),
updated_at: Set(chrono::Utc::now().naive_utc()),
..Default::default()
};
let Ok(_rent) = entity.save(&*db).await.inspect_err(|e| {
tracing::error!(
"Failed to create new rent for parking space {}: {e}",
parking_space.id
)
}) else {
return HttpResponse::BadRequest().body(render_rent_form(
form,
&req,
parking_space,
location,
Some(session),
hcx.clone(),
));
};
HttpResponse::SeeOther()
.append_header(("HX-Redirect", "/parking-spaces/all"))
.append_header(("Location", "/parking-spaces/all"))
.body("")
}
fn render_rent_form(
@ -568,7 +763,7 @@ fn render_rent_form(
req: &HttpRequest,
parking_space: ParkingSpace,
location: ParkingSpaceLocation,
session: MaybeAuthenticated,
session: Option<SessionOpts>,
hcx: HelperContext,
) -> String {
let body = ParkingSpaceRentFormTemplate {
@ -583,7 +778,7 @@ fn render_rent_form(
opts: MainOpts {
show: true,
search: None,
session: session.into_option().map(Into::into),
session,
},
hcx: hcx.clone(),
};

View File

@ -47,58 +47,7 @@
{% include "./single_parking_space.html" %}
{% include "./parking_space_state.html" %}
<div class="flex justify-end">
{% if parking_space.state == ParkingSpaceState::Verified %}
<a
class="text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center"
id="create-rent-{{parking_space.id}}"
hx-get="/parking-spaces/{{parking_space.id}}/parking-space-rents/form"
hx-headers='{"Accept":"text/html-partial"}'
>
<svg height="48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 171.439 171.439" xml:space="preserve" fill="#000000A">
<path style="fill:#000002;" d="M21.803,171.439c-0.463,0-0.922-0.129-1.322-0.378c-0.563-0.351-0.963-0.91-1.113-1.557L1.182,91.241 c-0.313-1.345,0.524-2.688,1.869-3.001l6.729-1.563l76.692-64.254l-4.435-4.012c-1.514,0.934-3.295,1.472-5.2,1.472 c-5.482,0-9.941-4.46-9.941-9.941S71.355,0,76.837,0s9.941,4.46,9.941,9.941c0,1.766-0.463,3.426-1.273,4.864l4.837,4.375 l1.117-0.936c0.902-0.757,2.21-0.781,3.141-0.058l48.281,37.561l6.189-1.438c1.347-0.309,2.688,0.525,3.001,1.87l18.187,78.263 c0.313,1.345-0.524,2.688-1.869,3.001L22.369,171.375C22.182,171.418,21.992,171.439,21.803,171.439z M6.618,92.545l17.054,73.394 l141.149-32.8l-17.055-73.393l-4.879,1.134c-0.022,0.006-0.045,0.011-0.068,0.016L11.556,91.397 c-0.041,0.011-0.082,0.021-0.124,0.029L6.618,92.545z M90.216,25.809L20.553,84.174L136.61,57.205L96.188,25.759 c-0.038,0.539-0.25,1.07-0.64,1.502c-0.925,1.024-2.506,1.104-3.531,0.178L90.216,25.809z M76.837,5 c-2.725,0-4.941,2.217-4.941,4.941s2.217,4.941,4.941,4.941c1.361,0,2.596-0.554,3.491-1.447c0.026-0.027,0.052-0.055,0.079-0.081 c0.849-0.888,1.372-2.091,1.372-3.413C81.778,7.217,79.562,5,76.837,5z M40.256,140.785c-1.135,0-2.162-0.778-2.432-1.931 l-6.447-27.504c-0.315-1.344,0.519-2.689,1.864-3.004c0.829-0.194,1.659,0.048,2.247,0.579c1.199-1.163,2.694-1.983,4.362-2.374 c2.429-0.569,4.937-0.16,7.058,1.156c2.121,1.315,3.603,3.378,4.173,5.809c1.176,5.016-1.949,10.055-6.965,11.23 c-0.386,0.091-0.773,0.156-1.158,0.196l12.745,7.904c1.173,0.729,1.534,2.27,0.807,3.442c-0.728,1.174-2.268,1.533-3.442,0.808 l-12.309-7.635l1.934,8.251c0.315,1.344-0.52,2.689-1.864,3.004C40.637,140.763,40.445,140.785,40.256,140.785z M41.992,111.303 c-0.333,0-0.668,0.039-1,0.116c-1.13,0.266-2.089,0.954-2.701,1.94c-0.612,0.986-0.803,2.151-0.538,3.281 c0.546,2.331,2.888,3.778,5.222,3.239c2.332-0.548,3.785-2.89,3.238-5.223c-0.265-1.13-0.954-2.089-1.94-2.7 C43.577,111.525,42.792,111.303,41.992,111.303z M67.849,133.016c-1.135,0-2.162-0.778-2.432-1.931l-2.969-12.666 c-0.003-0.011-0.005-0.021-0.008-0.033c-0.002-0.011-0.005-0.021-0.007-0.033l-3.14-13.396c0-0.002-0.001-0.005-0.002-0.008 c-0.315-1.344,0.52-2.689,1.864-3.004L76,98.466c1.343-0.315,2.689,0.52,3.004,1.863s-0.52,2.689-1.864,3.004l-12.411,2.909 l2.009,8.568l7.462-1.749c1.344-0.316,2.69,0.52,3.005,1.863c0.315,1.344-0.519,2.689-1.863,3.004l-7.463,1.75l1.835,7.831 l12.412-2.908c1.343-0.315,2.689,0.52,3.004,1.863s-0.52,2.689-1.864,3.004l-14.838,3.479c-0.002,0-0.005,0.001-0.007,0.001 C68.23,132.993,68.038,133.016,67.849,133.016z M93.49,127.005c-1.135,0-2.162-0.778-2.432-1.931l-6.126-26.136 c-0.219-0.933,0.116-1.865,0.791-2.455c0.929-1.001,2.494-1.075,3.51-0.155l18.833,17.021l-4.407-18.801 c-0.315-1.344,0.519-2.689,1.863-3.004c1.346-0.315,2.69,0.52,3.005,1.863l6.126,26.136c0.218,0.93-0.115,1.861-0.786,2.45 c-0.927,1.009-2.495,1.083-3.515,0.161L91.52,105.135l4.406,18.799c0.315,1.344-0.519,2.689-1.864,3.004 C93.871,126.982,93.679,127.005,93.49,127.005z M131.954,117.989c-1.135,0-2.162-0.778-2.432-1.931l-5.556-23.702l-6.93,1.624 c-1.344,0.313-2.69-0.52-3.004-1.863c-0.315-1.344,0.519-2.689,1.864-3.004l18.727-4.39c1.344-0.316,2.689,0.52,3.004,1.863 c0.315,1.344-0.519,2.689-1.864,3.004l-6.929,1.625l5.556,23.702c0.315,1.344-0.519,2.689-1.863,3.004 C132.335,117.967,132.143,117.989,131.954,117.989z">
</path>
</svg>
</a>
{% endif %}
{% if parking_space.state != ParkingSpaceState::Banned %}
<a
href="/parking-spaces/edit/{{parking_space.id}}"
hx-target="main"
hx-get="/parking-spaces/edit/{{parking_space.id}}"
hx-headers='{"Accept":"text/html-partial"}'
class="text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center"
>
<svg height="48" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="fill-white">
<path d="M20,16v4a2,2,0,0,1-2,2H4a2,2,0,0,1-2-2V6A2,2,0,0,1,4,4H8" class="stroke-blue-800" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
</path>
<polygon fill="none" points="12.5 15.8 22 6.2 17.8 2 8.3 11.5 8 16 12.5 15.8" class="stroke-blue-800" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
</polygon>
</svg>
</a>
<a
href="/parking-spaces/delete/{{parking_space.id}}"
hx-target="main"
hx-delete="/parking-spaces/edit/{{parking_space.id}}"
hx-headers='{"Accept":"text/html-partial"}'
class="text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center"
>
<svg viewBox="0 0 24 24" height="48" class="stroke-red-800 fill-white" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12V17" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M14 12V17" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M4 7H20" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M6 10V18C6 19.6569 7.34315 21 9 21H15C16.6569 21 18 19.6569 18 18V10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
</svg>
</a>
{% endif %}
</div>
{% include "./parking_space_actions.html" %}
</div>
{% endif %}
@ -118,16 +67,38 @@
{% if let Some(parking_space) = parking_space_by_id.get(parking_space_rent.parking_space_id) %}
{% if parking_space.state == ParkingSpaceState::Verified || Some(parking_space.account_id.clone()) == account_id %}
{% if let Some(account) = account_by_id.get(parking_space.account_id) %}
{% if let Some(location_id) = parking_space.location_id -%}
{% if let Some(location) = location_by_id.get(location_id) -%}
<oswilno-parking-space-rent id="parking-space-rent-{{ parking_space_rent.id }}">
<oswilno-parking-space id="parking-space-{{ parking_space.id }}">
<oswilno-parking-space
id="parking-space-{{ parking_space.id }}"
{% if let Some(spot) = parking_space.spot %} spot="{{ spot }}" {% endif %}
>
<oswilno-parking-space-location
id="parking-space-location-{{ location.id }}"
name="{{ location.name }}"
stage="{{ location.stage }}"
number="{{ location.number }}"
>
</oswilno-parking-space-location>
<oswilno-account id="account-{{ account.id }}">
<div>{{ account.login }}</div>
</oswilno-account>
</oswilno-parking-space>
<oswilno-price id="parking-space-rent-{{ parking_space_rent.id }}-price" multiplier="100" currency="PLN" price="{{ parking_space_rent.price }}">
<oswilno-price
id="parking-space-rent-{{ parking_space_rent.id }}-price"
multiplier="100"
currency="PLN"
price="{{ parking_space_rent.price }}"
>
{{ parking_space_rent.price }}
</oswilno-price>
</oswilno-parking-space-rent>
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,51 @@
<div class="flex justify-end">
{% if parking_space.state == ParkingSpaceState::Verified %}
<a
class="text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center"
id="create-rent-{{parking_space.id}}"
hx-get="/parking-spaces/{{parking_space.id}}/parking-space-rents/form"
hx-headers='{"Accept":"text/html-partial"}'
>
<svg height="32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 171.439 171.439" xml:space="preserve" fill="#000000A">
<path style="fill:#000002;" d="M21.803,171.439c-0.463,0-0.922-0.129-1.322-0.378c-0.563-0.351-0.963-0.91-1.113-1.557L1.182,91.241 c-0.313-1.345,0.524-2.688,1.869-3.001l6.729-1.563l76.692-64.254l-4.435-4.012c-1.514,0.934-3.295,1.472-5.2,1.472 c-5.482,0-9.941-4.46-9.941-9.941S71.355,0,76.837,0s9.941,4.46,9.941,9.941c0,1.766-0.463,3.426-1.273,4.864l4.837,4.375 l1.117-0.936c0.902-0.757,2.21-0.781,3.141-0.058l48.281,37.561l6.189-1.438c1.347-0.309,2.688,0.525,3.001,1.87l18.187,78.263 c0.313,1.345-0.524,2.688-1.869,3.001L22.369,171.375C22.182,171.418,21.992,171.439,21.803,171.439z M6.618,92.545l17.054,73.394 l141.149-32.8l-17.055-73.393l-4.879,1.134c-0.022,0.006-0.045,0.011-0.068,0.016L11.556,91.397 c-0.041,0.011-0.082,0.021-0.124,0.029L6.618,92.545z M90.216,25.809L20.553,84.174L136.61,57.205L96.188,25.759 c-0.038,0.539-0.25,1.07-0.64,1.502c-0.925,1.024-2.506,1.104-3.531,0.178L90.216,25.809z M76.837,5 c-2.725,0-4.941,2.217-4.941,4.941s2.217,4.941,4.941,4.941c1.361,0,2.596-0.554,3.491-1.447c0.026-0.027,0.052-0.055,0.079-0.081 c0.849-0.888,1.372-2.091,1.372-3.413C81.778,7.217,79.562,5,76.837,5z M40.256,140.785c-1.135,0-2.162-0.778-2.432-1.931 l-6.447-27.504c-0.315-1.344,0.519-2.689,1.864-3.004c0.829-0.194,1.659,0.048,2.247,0.579c1.199-1.163,2.694-1.983,4.362-2.374 c2.429-0.569,4.937-0.16,7.058,1.156c2.121,1.315,3.603,3.378,4.173,5.809c1.176,5.016-1.949,10.055-6.965,11.23 c-0.386,0.091-0.773,0.156-1.158,0.196l12.745,7.904c1.173,0.729,1.534,2.27,0.807,3.442c-0.728,1.174-2.268,1.533-3.442,0.808 l-12.309-7.635l1.934,8.251c0.315,1.344-0.52,2.689-1.864,3.004C40.637,140.763,40.445,140.785,40.256,140.785z M41.992,111.303 c-0.333,0-0.668,0.039-1,0.116c-1.13,0.266-2.089,0.954-2.701,1.94c-0.612,0.986-0.803,2.151-0.538,3.281 c0.546,2.331,2.888,3.778,5.222,3.239c2.332-0.548,3.785-2.89,3.238-5.223c-0.265-1.13-0.954-2.089-1.94-2.7 C43.577,111.525,42.792,111.303,41.992,111.303z M67.849,133.016c-1.135,0-2.162-0.778-2.432-1.931l-2.969-12.666 c-0.003-0.011-0.005-0.021-0.008-0.033c-0.002-0.011-0.005-0.021-0.007-0.033l-3.14-13.396c0-0.002-0.001-0.005-0.002-0.008 c-0.315-1.344,0.52-2.689,1.864-3.004L76,98.466c1.343-0.315,2.689,0.52,3.004,1.863s-0.52,2.689-1.864,3.004l-12.411,2.909 l2.009,8.568l7.462-1.749c1.344-0.316,2.69,0.52,3.005,1.863c0.315,1.344-0.519,2.689-1.863,3.004l-7.463,1.75l1.835,7.831 l12.412-2.908c1.343-0.315,2.689,0.52,3.004,1.863s-0.52,2.689-1.864,3.004l-14.838,3.479c-0.002,0-0.005,0.001-0.007,0.001 C68.23,132.993,68.038,133.016,67.849,133.016z M93.49,127.005c-1.135,0-2.162-0.778-2.432-1.931l-6.126-26.136 c-0.219-0.933,0.116-1.865,0.791-2.455c0.929-1.001,2.494-1.075,3.51-0.155l18.833,17.021l-4.407-18.801 c-0.315-1.344,0.519-2.689,1.863-3.004c1.346-0.315,2.69,0.52,3.005,1.863l6.126,26.136c0.218,0.93-0.115,1.861-0.786,2.45 c-0.927,1.009-2.495,1.083-3.515,0.161L91.52,105.135l4.406,18.799c0.315,1.344-0.519,2.689-1.864,3.004 C93.871,126.982,93.679,127.005,93.49,127.005z M131.954,117.989c-1.135,0-2.162-0.778-2.432-1.931l-5.556-23.702l-6.93,1.624 c-1.344,0.313-2.69-0.52-3.004-1.863c-0.315-1.344,0.519-2.689,1.864-3.004l18.727-4.39c1.344-0.316,2.689,0.52,3.004,1.863 c0.315,1.344-0.519,2.689-1.864,3.004l-6.929,1.625l5.556,23.702c0.315,1.344-0.519,2.689-1.863,3.004 C132.335,117.967,132.143,117.989,131.954,117.989z">
</path>
</svg>
</a>
{% endif %}
{% if parking_space.state != ParkingSpaceState::Banned %}
<a
href="/parking-spaces/edit/{{parking_space.id}}"
hx-target="main"
hx-get="/parking-spaces/edit/{{parking_space.id}}"
hx-headers='{"Accept":"text/html-partial"}'
class="text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center"
>
<svg height="32" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="fill-white">
<path d="M20,16v4a2,2,0,0,1-2,2H4a2,2,0,0,1-2-2V6A2,2,0,0,1,4,4H8" class="stroke-blue-800" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
</path>
<polygon fill="none" points="12.5 15.8 22 6.2 17.8 2 8.3 11.5 8 16 12.5 15.8" class="stroke-blue-800" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
</polygon>
</svg>
</a>
<a
href="/parking-spaces/delete/{{parking_space.id}}"
hx-target="main"
hx-delete="/parking-spaces/edit/{{parking_space.id}}"
hx-headers='{"Accept":"text/html-partial"}'
class="text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm p-2.5 text-center inline-flex items-center"
>
<svg viewBox="0 0 24 24" height="32" class="stroke-red-800 fill-white" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12V17" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M14 12V17" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M4 7H20" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M6 10V18C6 19.6569 7.34315 21 9 21H15C16.6569 21 18 19.6569 18 18V10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
<path d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
</path>
</svg>
</a>
{% endif %}
</div>

View File

@ -1,3 +1,4 @@
#![feature(result_option_inspect)]
use std::sync::Arc;
use std::time::Duration;

View File

@ -1,3 +1,4 @@
#![feature(result_option_inspect)]
use std::sync::Arc;
pub use actix_jwt_session::*;
@ -7,12 +8,11 @@ use actix_web::{get, post, HttpRequest, HttpResponse};
use askama_actix::Template;
use autometrics::autometrics;
use garde::Validate;
pub use oswilno_view::filters;
use oswilno_view::{Blank, Errors, HelperContext, Layout, Main, MainOpts};
use sea_orm::DatabaseConnection;
use serde::{Deserialize, Serialize};
pub use oswilno_view::filters;
pub type Authenticated = actix_jwt_session::Authenticated<Claims>;
pub type MaybeAuthenticated = actix_jwt_session::MaybeAuthenticated<Claims>;
@ -239,7 +239,6 @@ async fn login_inner(
errors: &mut Errors,
) -> Option<HttpResponse> {
let iat = OffsetDateTime::now_utc().unix_timestamp() as usize;
let account = find_account(payload, errors, db.clone()).await?;
if let Err(e) = Hashing::verify(account.pass_hash.as_str(), payload.password.as_str()) {
@ -347,27 +346,27 @@ async fn refresh_token(
_hcx: HelperContext,
) -> HttpResponse {
let s = storage.into_inner();
let pair = match s.refresh::<Claims>(refresh_token.access_jti()).await {
Err(e) => {
tracing::warn!("Failed to refresh token: {e}");
let Ok(pair) = s
.refresh::<Claims>(refresh_token.access_jti())
.await
.map_err(|e| tracing::warn!("Failed to refresh token: {e}"))
else {
return HttpResponse::BadRequest().finish();
}
Ok(pair) => pair,
};
let encrypted_jwt = match pair.jwt.encode() {
Ok(text) => text,
Err(e) => {
tracing::warn!("Failed to encode claims: {e}");
let Ok(encrypted_jwt) = pair
.jwt
.encode()
.inspect_err(|e| tracing::warn!("Failed to encode claims: {e}"))
else {
return HttpResponse::InternalServerError().finish();
}
};
let encrypted_refresh = match pair.refresh.encode() {
Err(e) => {
tracing::warn!("Failed to encode claims: {e}");
let Ok(encrypted_refresh) = pair
.refresh
.encode()
.inspect_err(|e| tracing::warn!("Failed to encode claims: {e}"))
else {
return HttpResponse::InternalServerError().finish();
}
Ok(text) => text,
};
HttpResponse::Ok()
.append_header((
@ -600,12 +599,9 @@ async fn register_internal(
}
tracing::warn!("{errors:#?}");
let pass = match Hashing::encrypt(p.password.as_str()) {
Ok(p) => p,
Err(e) => {
tracing::warn!("{e}");
let Ok(pass) = Hashing::encrypt(p.password.as_str()).inspect_err(|e| tracing::warn!("{e}"))
else {
return Ok(HttpResponse::InternalServerError().body(""));
}
};
let model = (ActiveModel {
id: NotSet,

View File

@ -1,6 +1,7 @@
use crate::HelperContext;
use askama::Result;
use crate::HelperContext;
#[tracing::instrument(skip(htx))]
pub fn t<T: std::fmt::Display + std::fmt::Debug>(s: T, htx: &HelperContext) -> Result<String> {
// tracing::debug!("translating {s:?} to lang {lang:?} with {t:?}");

View File

@ -1,12 +1,13 @@
use crate::Lang;
use crate::{ExtractLangFuture, TranslationStorage};
use actix_web::error::Error;
use actix_web::web::Data;
use actix_web::HttpRequest;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_web::error::Error;
use actix_web::web::Data;
use actix_web::HttpRequest;
use crate::{ExtractLangFuture, Lang, TranslationStorage};
#[derive(Clone, Debug)]
pub struct HelperContext {
pub t: Data<TranslationStorage>,

View File

@ -1,10 +1,11 @@
use actix_web::error::Error;
use actix_web::HttpRequest;
use std::future::Future;
use std::pin::Pin;
use std::str::FromStr;
use std::task::{Context, Poll};
use actix_web::error::Error;
use actix_web::HttpRequest;
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug, Default)]
pub enum Lang {
#[default]

View File

@ -4,7 +4,6 @@ use std::sync::{Arc, RwLock};
use actix_web::http::header::ContentType;
use actix_web::web::ServiceConfig;
use actix_web::{get, HttpRequest, HttpResponse};
pub use helper_context::*;
pub use lang::*;

View File

@ -11,7 +11,7 @@
type="search"
id="search-navbar"
class="block w-full min-w-[35rem] p-2 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder='{"Search.."|t(hcx)}'
placeholder='{{ "Search.."|t(hcx) }}'
>
</div>
</div>

View File

@ -1,6 +1,10 @@
import './elements/oswilno-price.js';
import './elements/oswilno-error.js';
import './elements/oswilno-parking-space-rents.js';
import './elements/oswilno-parking-space-rent.js';
import './elements/oswilno-parking-space.js';
import './elements/oswilno-parking-space-location.js';
import("https://unpkg.com/htmx.org@1.9.4/dist/htmx.min.js");
const READ_AUTH_HEADER = 'Authorization';

View File

@ -0,0 +1,14 @@
customElements.define('oswilno-parking-space-location', class extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host { display: block; width: 100%; }
</style>
<div>
<slot></slot>
</div>
`;
}
});

View File

@ -0,0 +1,14 @@
customElements.define('oswilno-parking-space-rent', class extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host { display: block; width: 100%; }
</style>
<div class="w-[48%] p-4 bg-white border border-white-200 rounded-lg shadow sm:p-8 dark:bg-white-800 dark:border-white-700 mt-6">
<slot></slot>
</div>
`;
}
});

View File

@ -0,0 +1,23 @@
customElements.define('oswilno-parking-space-rents', class extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
:host {
display: block;
width: 100%;
}
#container {
display: flex;
justify-content: space-between;
width: 100%;
}
</style>
<div id="container">
<slot></slot>
</div>
`;
}
});

View File

@ -6,5 +6,6 @@ fn main() {
// .stdout(Stdio::piped())
// .spawn()
// .expect("Failed to create child process for building JS");
// child.wait_with_output().expect("Failed to run child process for building JS");
// child.wait_with_output().expect("Failed to run child process for building
// JS");
}

View File

@ -1,6 +1,8 @@
use std::{borrow::Cow, sync::Once};
use std::borrow::Cow;
use std::sync::Once;
use actix_web::{get, web::ServiceConfig, HttpResponse};
use actix_web::web::ServiceConfig;
use actix_web::{get, HttpResponse};
pub fn mount(config: &mut ServiceConfig) {
config

View File

@ -4,6 +4,7 @@ sea-orm-cli generate entity -v -l --expanded-format -o ./crates/oswilno-contract
echo 'pub use sea_orm;' >> ./crates/oswilno-contract/src/lib.rs
echo 'pub use actix_admin;' >> ./crates/oswilno-contract/src/lib.rs
echo "pub use ::chrono;" >> ./crates/oswilno-contract/src/lib.rs
for f in $( ls ./crates/oswilno-contract/src/*.rs ); do
# bad generated code patch
@ -29,6 +30,7 @@ for f in $(ls crates/oswilno-contract/src/*.rs); do
fi
;;
*"struct Model"* )
# echo "#[derive(DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel, DeriveActixAdminModelSelectList)]" >> /tmp/ajosdfjosdpjfojsdfjpajpfa
echo "#[derive(DeriveActixAdmin, DeriveActixAdminModel, DeriveActixAdminViewModel)]" >> /tmp/ajosdfjosdpjfojsdfjpajpfa
;;
"pub id: i32," )