diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..01ac157 --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +{"version":1,"defects":[],"times":{"LukasBestle\\Versions\\ChangesTest::testNoChanges":0.016,"LukasBestle\\Versions\\ChangesTest::testWithChanges":0.056,"LukasBestle\\Versions\\InstanceTest::testChanges":0.014,"LukasBestle\\Versions\\InstanceTest::testConstructErrorNoGitRepo":0.015,"LukasBestle\\Versions\\InstanceTest::testCreateVersion":0.308,"LukasBestle\\Versions\\InstanceTest::testCreateVersionErrorNoChanges":0.014,"LukasBestle\\Versions\\InstanceTest::testIsCurrent":0.013,"LukasBestle\\Versions\\InstanceTest::testMeta":0.012,"LukasBestle\\Versions\\InstanceTest::testPrepareCreation":0.072,"LukasBestle\\Versions\\InstanceTest::testPrepareCreationErrorLocks":0.067,"LukasBestle\\Versions\\InstanceTest::testPrepareCreationErrorNoChanges":0.013,"LukasBestle\\Versions\\InstanceTest::testToArray":0.146,"LukasBestle\\Versions\\InstanceTest::testVersion":0.142,"LukasBestle\\Versions\\InstancesTest::testConstruct1NoInstances":0.032,"LukasBestle\\Versions\\InstancesTest::testConstruct2AllInstancesConfigured":0.051,"LukasBestle\\Versions\\InstancesTest::testConstruct3OnlySecondInstance":0.049,"LukasBestle\\Versions\\InstancesTest::testConstructErrorInvalidGitOutput":0.199,"LukasBestle\\Versions\\InstancesTest::testConstructErrorNoWorktree":0.075,"LukasBestle\\Versions\\InstancesTest::testConstructErrorOnBranch":0.04,"LukasBestle\\Versions\\InstancesTest::testFindOrException":0.023,"LukasBestle\\Versions\\InstancesTest::testFindOrExceptionErrorNotFound":0.024,"LukasBestle\\Versions\\PluginTest::testCheckPermission":0.004,"LukasBestle\\Versions\\PluginTest::testCheckPermissionInvalid":0.004,"LukasBestle\\Versions\\PluginTest::testCheckPermissionNoUser":0.003,"LukasBestle\\Versions\\PluginTest::testCleanExports":0.008,"LukasBestle\\Versions\\PluginTest::testGitCommand":0.054,"LukasBestle\\Versions\\PluginTest::testGitCommandError":0.022,"LukasBestle\\Versions\\PluginTest::testHasPermission":0.003,"LukasBestle\\Versions\\PluginTest::testHasPermissionNoUser":0.007,"LukasBestle\\Versions\\PluginTest::testInstance":0.005,"LukasBestle\\Versions\\PluginTest::testInstances":0.056,"LukasBestle\\Versions\\PluginTest::testInstancesErrorValidate":0.157,"LukasBestle\\Versions\\PluginTest::testOptions":0.003,"LukasBestle\\Versions\\PluginTest::testToApiData":0.069,"LukasBestle\\Versions\\PluginTest::testToApiDataNoUser":0.004,"LukasBestle\\Versions\\PluginTest::testToArray":0.083,"LukasBestle\\Versions\\PluginTest::testValidate":0.038,"LukasBestle\\Versions\\PluginTest::testValidateErrorGitTooOldAndOnlyOnce":0.008,"LukasBestle\\Versions\\PluginTest::testValidateErrorGitUnparseable":0.157,"LukasBestle\\Versions\\PluginTest::testValidateErrorInstances":0.024,"LukasBestle\\Versions\\PluginTest::testVersions":0.048,"LukasBestle\\Versions\\PluginTest::testVersionsErrorValidate":0.009,"LukasBestle\\Versions\\VersionTest::testDelete":0.11,"LukasBestle\\Versions\\VersionTest::testDeleteErrorInUse":0.049,"LukasBestle\\Versions\\VersionTest::testDeployTo":0.317,"LukasBestle\\Versions\\VersionTest::testDeployToErrorCannotAutosave":0.121,"LukasBestle\\Versions\\VersionTest::testExport":0.063,"LukasBestle\\Versions\\VersionTest::testInstances":0.166,"LukasBestle\\Versions\\VersionTest::testMeta":0.003,"LukasBestle\\Versions\\VersionTest::testSetCreatedError1":0.004,"LukasBestle\\Versions\\VersionTest::testSetCreatedError2":0.004,"LukasBestle\\Versions\\VersionTest::testToArray":0.05,"LukasBestle\\Versions\\VersionsTest::testAutodelete":0.344,"LukasBestle\\Versions\\VersionsTest::testConstruct":0.014,"LukasBestle\\Versions\\VersionsTest::testFindOrException":0.016,"LukasBestle\\Versions\\VersionsTest::testFindOrExceptionErrorNotFound":0.013,"LukasBestle\\Versions\\VersionsTest::testUpdate":0.209}} \ No newline at end of file diff --git a/composer.json b/composer.json index 6149436..ad452a1 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,15 @@ { - "name": "lukasbestle/kirby-versions", + "name": "nerdcel/kirby-versions", "description": "Versions Plugin for Kirby", "license": "MIT", "type": "kirby-plugin", - "version": "2.0.1", "authors": [ { "name": "Lukas Bestle", "email": "project-kirbyversions@lukasbestle.com" + }, { + "name": "Marcel Hieke", + "email": "office@marcelhieke.com" } ], "require": { @@ -27,7 +29,7 @@ } }, "extra": { - "installer-name": "versions", + "installer-name": "kirby-versions-extended", "kirby-cms-path": false }, "scripts": { diff --git a/index.css b/index.css index 936035c..1eb09bb 100644 --- a/index.css +++ b/index.css @@ -1 +1,202 @@ -.lbvs-changes li{padding-left:1.2em;position:relative}.lbvs-changes li span{position:absolute;left:0;font-family:var(--font-mono);font-weight:700}.lbvs-changes li span[data-status="+"],.lbvs-changes li span[data-status=C]{color:var(--color-positive)}.lbvs-changes li span[data-status="-"]{color:var(--color-negative)}.lbvs-changes li span[data-status=M],.lbvs-changes li span[data-status=R]{color:var(--color-notice)}.lbvs-create-error-dialog{line-height:1.5}.lbvs-create-error-dialog-message{margin-bottom:1rem;color:var(--color-negative)}.lbvs-create-error-dialog-list li{margin-left:1.2rem;list-style:disc}.lbvs-create-error-dialog-list span{color:var(--color-text-light)}.lbvs-create-changes{margin-bottom:2.25rem}.lbvs-version-delete-dialog{line-height:1.5}.lbvs-instance-name{display:inline-block;padding:0 .3em;border-radius:var(--rounded);color:var(--color-text)}strong.lbvs-instance-name{padding:.2em .4em}.lbvs-instance-names-cell{line-height:1.5;padding:.325rem .75rem}.lbvs-instance-names-cell .lbvs-instance-name{margin-top:.1625rem;margin-bottom:.1625rem;margin-right:.325rem}.lbvs-status{padding-top:1.5rem;padding-bottom:2rem;background:#2b2b2b;color:var(--color-white);border-radius:var(--rounded)}.lbvs-status .k-grid{grid-row-gap:1.5rem}.lbvs-status-instances li{padding:.8rem}.lbvs-status-instances li.current{background:var(--color-background);border-radius:var(--rounded);color:var(--color-text)}.lbvs-status-current{display:inline-block;margin-left:.5rem;padding:.1em .3em;border:1px solid var(--color-border);border-radius:var(--rounded-sm);font-size:var(--font-size-small)}.lbvs-status-instances .lbvs-version{margin-top:.4rem}.lbvs-status-changes{display:flex;flex-direction:column;height:100%}.lbvs-status-changes .lbvs-changes{height:100%;padding:.8rem;background:var(--color-background);border-radius:var(--rounded);color:var(--color-text)}@media screen and (min-width: 30em){.lbvs-status-instances li.current{border-top-right-radius:0;border-bottom-right-radius:0}.lbvs-status-changes .lbvs-changes{border-top-left-radius:0;border-bottom-left-radius:0}}.lbvs-version{line-height:1.4}.lbvs-version-details{font-size:var(--font-size-small)}.lbvs-version-details dd{display:inline}.lbvs-version-details dd:not(:last-child):after{content:" · ";color:var(--color-text-light)}.lbvs-version-label-cell{padding:.325rem .75rem}.lbvs-versions{padding-top:1.5rem}.lbvs-version-dialog .lbvs-version{margin-bottom:1.5rem}.lbvs-view>.k-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)} + +.lbvs-changes li { + padding-left: 1.2em; + position: relative; + display: flex; +} +.lbvs-changes li > span { + position: absolute; + left: 0; + + font-family: var(--font-mono); + font-weight: bold; +} +.lbvs-changes li span[data-status="+"], +.lbvs-changes li span[data-status="C"] { + color: var(--color-positive); +} +.lbvs-changes li span[data-status="-"] { + color: var(--color-negative); +} +.lbvs-changes li span[data-status="M"], +.lbvs-changes li span[data-status="R"] { + color: var(--color-notice); +} +.lbvs-changes { + font-family: var(--font-family-mono); + display: flex; + flex-direction: column; + gap: 4px; + max-height: 45svh; + min-height: 400px; + overflow: auto; +} +.lbvs-changes .k-input { + min-height: unset; + margin: 0 7px 0 0; + background: none; + outline: none; + border: none; +} +.lbvs-changes label { + line-height: 1.2; +} +.lbvs-changes li { + padding-left: 1.2em; + position: relative; + display: flex; +} +.lbvs-changes li > span { + position: absolute; + left: 0; + font-weight: bold; +} +.lbvs-changes li span[data-status="+"], +.lbvs-changes li span[data-status=C] { + color: var(--color-positive); +} +.lbvs-changes li span[data-status="-"] { + color: var(--color-negative); +} +.lbvs-changes li span[data-status=M], +.lbvs-changes li span[data-status=R] { + color: var(--color-notice); +} +.lbvs-create-error-dialog { + line-height: 1.5; +} +.lbvs-create-error-dialog-message { + margin-bottom: 1rem; + + color: var(--color-negative); +} +.lbvs-create-error-dialog-list li { + margin-left: 1.2rem; + + list-style: disc; +} +.lbvs-create-error-dialog-list span { + color: var(--color-text-light); +} + +.lbvs-create-changes { + margin-bottom: 2.25rem; +} + +.lbvs-version-delete-dialog { + line-height: 1.5; +} + +.lbvs-instance-name { + display: inline-block; + padding: 0 0.3em; + + border-radius: var(--rounded); + color: var(--color-text); +} +strong.lbvs-instance-name { + padding: 0.2em 0.4em; +} + +.lbvs-instance-names-cell { + line-height: 1.5; + padding: 0.325rem 0.75rem; +} +.lbvs-instance-names-cell .lbvs-instance-name { + margin-top: 0.1625rem; + margin-bottom: 0.1625rem; + margin-right: 0.325rem; +} + +.lbvs-status { + padding-top: 1.5rem; + padding-bottom: 2rem; + + background: #2b2b2b; + color: var(--color-white); + border-radius: var(--rounded); +} +.lbvs-status .k-grid { + /* gap between the columns on mobile */ + grid-row-gap: 1.5rem; +} +.lbvs-status-instances li { + padding: 0.8rem; +} +.lbvs-status-instances li.current { + background: var(--color-background); + border-radius: var(--rounded); + color: var(--color-text); +} +.lbvs-status-current { + display: inline-block; + margin-left: 0.5rem; + padding: 0.1em 0.3em; + + border: 1px solid var(--color-border); + border-radius: var(--rounded-sm); + + font-size: var(--font-size-small); +} +.lbvs-status-instances .lbvs-version { + margin-top: 0.4rem; +} +.lbvs-status-changes { + /* ensure the list of changes fills the whole view vertically */ + display: flex; + flex-direction: column; + height: 100%; +} +.lbvs-status-changes .lbvs-changes { + height: 100%; + padding: 0.8rem; + + background: var(--color-background); + border-radius: var(--rounded); + color: var(--color-text); +} + +/* no rounded corners between instances and changes columns, + unless the columns are displayed vertically (on mobile) */ +@media screen and (min-width: 30em) { +.lbvs-status-instances li.current { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.lbvs-status-changes .lbvs-changes { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +} + +.lbvs-version { + line-height: 1.4; +} +.lbvs-version-details { + font-size: var(--font-size-small); +} +.lbvs-version-details dd { + display: inline; +} +.lbvs-version-details dd:not(:last-child)::after { + content: " · "; + + color: var(--color-text-light); +} + +.lbvs-version-label-cell { + padding: 0.325rem 0.75rem; +} + +.lbvs-versions { + padding-top: 1.5rem; +} +.lbvs-version-dialog .lbvs-version { + margin-bottom: 1.5rem; +} +.lbvs-view > .k-loader { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/index.js b/index.js index fda1e42..39c4411 100644 --- a/index.js +++ b/index.js @@ -1 +1,918 @@ -(function(){"use strict";function i(t,e,s,n,r,c,u,xe){var a=typeof t=="function"?t.options:t;e&&(a.render=e,a.staticRenderFns=s,a._compiled=!0),n&&(a.functional=!0),c&&(a._scopeId="data-v-"+c);var l;if(u?(l=function(o){o=o||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,!o&&typeof __VUE_SSR_CONTEXT__<"u"&&(o=__VUE_SSR_CONTEXT__),r&&r.call(this,o),o&&o._registeredComponents&&o._registeredComponents.add(u)},a._ssrRegister=l):r&&(l=xe?function(){r.call(this,(a.functional?this.parent:this).$root.$options.shadowRoot)}:r),l)if(a.functional){a._injectStyles=l;var De=a.render;a.render=function(Se,d){return l.call(d),De(Se,d)}}else{var v=a.beforeCreate;a.beforeCreate=v?[].concat(v,l):[l]}return{exports:t,options:a}}const _={props:{changes:Object}};var f=function(){var e=this,s=e._self._c;return s("ul",{staticClass:"lbvs-changes"},e._l(e.changes,function(n,r){return s("li",{key:r},[s("span",{attrs:{"data-status":n,title:e.$t("versions.label.status."+n)}},[e._v(" "+e._s(n)+" ")]),e._v(" "+e._s(r)+" ")])}),0)},h=[],b=i(_,f,h,!1,null,null,null,null);const p=b.exports,m={data(){return{error:{message:null,details:{lockedModels:{}}}}},methods:{open(t){this.error=t,this.$refs.dialog.open()}}};var g=function(){var e=this,s=e._self._c;return s("k-dialog",{ref:"dialog",staticClass:"lbvs-create-error-dialog",attrs:{"cancel-button":e.$t("close"),"submit-button":!1}},[s("p",{staticClass:"lbvs-create-error-dialog-message"},[e._v(" "+e._s(e.error.message)+" ")]),s("ul",{staticClass:"lbvs-create-error-dialog-list"},e._l(e.error.details.lockedModels,function(n,r){return s("li",{key:r},[e._v(" "+e._s(r)+" "),s("span",[e._v("("+e._s(n.join(", "))+")")])])}),0)])},$=[],y=i(m,g,$,!1,null,null,null,null);const C=y.exports,k={data(){return{instance:null,inProgress:!1,stagedChanges:{}}},computed:{fields(){return{label:{autofocus:!0,icon:"title",label:this.$t("versions.label.label"),type:"text"}}}},methods:{async onSubmit(){if(this.inProgress!==!0)try{this.inProgress=!0;let t=this.$refs.form.value.label;if(!t)throw this.$t("field.required");await this.$store.dispatch({type:"versions/createVersion",instance:this.instance,label:t}),this.$store.dispatch("notification/success",":)"),this.$refs.dialog.close()}catch(t){this.$refs.dialog.error(t.message||t)}finally{this.inProgress=!1}},async open(t){this.instance=t;try{this.stagedChanges=await this.$store.dispatch({type:"versions/prepareVersionCreation",instance:this.instance})}catch(e){if(e.key==="error.versions.lockFiles")return this.$refs.errorDialog.open(e);throw e}this.$refs.dialog.open()}}};var w=function(){var e=this,s=e._self._c;return s("div",[s("k-dialog",{ref:"dialog",attrs:{size:"large","submit-button":e.$t("versions.button.create"),theme:"positive"},on:{submit:e.onSubmit}},[s("k-form",{ref:"form",attrs:{fields:e.fields},on:{submit:e.onSubmit},scopedSlots:e._u([{key:"header",fn:function(){return[s("k-field",{staticClass:"lbvs-create-changes",attrs:{label:e.$t("versions.label.changes")}},[s("lbvs-changes",{attrs:{changes:e.stagedChanges}})],1)]},proxy:!0}])})],1),s("lbvs-create-error-dialog",{ref:"errorDialog"})],1)},x=[],D=i(k,w,x,!1,null,null,null,null);const S=D.exports,R={data(){return{inProgress:!1,version:null}},methods:{async onSubmit(){if(this.inProgress!==!0)try{this.inProgress=!0,await this.$store.dispatch({type:"versions/deleteVersion",version:this.version.name}),this.$store.dispatch("notification/success",":)"),this.$refs.dialog.close()}catch(t){this.$refs.dialog.error(t.message||t)}finally{this.inProgress=!1}},open(t){this.version=t,this.$refs.dialog.open()}}};var T=function(){var e=this,s=e._self._c;return s("k-dialog",{ref:"dialog",staticClass:"lbvs-version-dialog lbvs-version-delete-dialog",attrs:{icon:"trash","submit-button":e.$t("versions.button.delete"),theme:"negative"},on:{submit:e.onSubmit}},[e.version?s("lbvs-version",{attrs:{version:e.version}}):e._e(),s("p",[e._v(e._s(e.$t("versions.message.delete")))])],1)},V=[],F=i(R,T,V,!1,null,null,null,null);const O=F.exports,A={data(){return{inProgress:!1,version:null}},computed:{fields(){let t=this.$store.getters["versions/currentInstance"],e=Object.values(this.$store.state.versions.data.instances).map(s=>({text:s.name,value:s.name}));return{instance:{disabled:e.length<=1,empty:!1,icon:"box",label:this.$t("versions.label.targetInstance"),options:e,placeholder:t.name,type:"select",value:t.name}}}},methods:{async onSubmit(){if(this.inProgress!==!0)try{this.inProgress=!0;let t=this.$refs.form.value.instance||this.$store.getters["versions/currentInstance"].name;await this.$store.dispatch({type:"versions/deployVersion",version:this.version.name,instance:t}),this.$store.dispatch("notification/success",":)"),this.$refs.dialog.close()}catch(t){this.$refs.dialog.error(t.message||t)}finally{this.inProgress=!1}},open(t){this.version=t,this.$refs.dialog.open()}}};var P=function(){var e=this,s=e._self._c;return s("k-dialog",{ref:"dialog",staticClass:"lbvs-version-dialog",attrs:{"submit-button":e.$t("versions.button.deploy"),theme:"positive"},on:{submit:e.onSubmit}},[e.version?s("lbvs-version",{attrs:{version:e.version}}):e._e(),s("k-form",{ref:"form",attrs:{fields:e.fields},on:{submit:e.onSubmit}})],1)},j=[],I=i(A,P,j,!1,null,null,null,null);const E=I.exports,L={data(){return{data:null,version:{}}},computed:{details(){return this.data?[{title:this.$t("versions.label.fileSize"),value:this.data.filesize}]:[]},supportsClipboard(){try{return window.navigator.clipboard.writeText,!0}catch{return!1}}},methods:{async copyToClipboard(){await window.navigator.clipboard.writeText(this.data.url),this.$store.dispatch("notification/success",":)")},download(){window.location=this.data.url,this.$store.dispatch("notification/success",":)")},async open(t){this.data=null,this.version=t,this.$refs.dialog.open();let e=await this.$store.dispatch({type:"versions/exportVersion",version:this.version.name});t===this.version&&(this.data=e)}}};var N=function(){var e=this,s=e._self._c;return s("k-dialog",{ref:"dialog",staticClass:"lbvs-version-dialog",attrs:{"cancel-button":e.$t(e.data?"close":"cancel"),"submit-button":!1}},[e.version?s("lbvs-version",{attrs:{details:e.details,version:e.version}}):e._e(),e.data?s("k-button-group",[s("k-button",{attrs:{icon:"download"},on:{click:e.download}},[e._v(" "+e._s(e.$t("versions.button.download"))+" ")]),s("k-button",{attrs:{icon:"copy",disabled:!e.supportsClipboard},on:{click:e.copyToClipboard}},[e._v(" "+e._s(e.$t("versions.button.copyLink"))+" ")])],1):s("p",[e._v(" "+e._s(e.$t("versions.message.exporting"))+" ")])],1)},z=[],M=i(L,N,z,!1,null,null,null,null);const Y=M.exports,B={props:{inline:Boolean,instance:[Object,String]},computed:{element(){return this.inline?"span":"strong"},instanceObj(){return typeof this.instance=="string"?{name:this.instance,color:"var(--color-gray-300)"}:this.instance}}};var H=function(){var e=this,s=e._self._c;return s(e.element,{tag:"component",staticClass:"lbvs-instance-name",style:{backgroundColor:e.instanceObj.color}},[e._v(" "+e._s(e.instanceObj.name)+" ")])},U=[],X=i(B,H,U,!1,null,null,null,null);const q=X.exports,W={props:{value:[Array,String]},computed:{instances(){return Array.isArray(this.value)?this.value:[this.value]}}};var G=function(){var e=this,s=e._self._c;return s("div",{staticClass:"lbvs-instance-names-cell"},e._l(e.instances,function(n){return s("lbvs-instance-name",{key:n,attrs:{inline:!0,instance:e.$store.state.versions.data.instances[n]||n}})}),1)},J=[],K=i(W,G,J,!1,null,null,null,null);const Q=K.exports,Z={computed:{canCreateVersion(){return this.$permissions["lukasbestle.versions"].create===!0&&Object.keys(this.currentChanges).length>0},currentChanges(){return this.$store.getters["versions/currentInstance"].changes}},methods:{onCreate(){let t=this.$store.getters["versions/currentInstance"].name;return this.$refs.createDialog.open(t)}}};var ee=function(){var e=this,s=e._self._c;return s("div",{staticClass:"lbvs-status"},[s("k-view",[s("k-grid",[s("k-column",{attrs:{width:"1/3"}},[s("header",{staticClass:"k-section-header"},[s("k-label",{attrs:{type:"section"}},[e._v(" "+e._s(e.$t("versions.label.instances"))+" ")])],1),s("ul",{staticClass:"lbvs-status-instances"},e._l(e.$store.state.versions.data.instances,function(n){return s("li",{key:n.name,class:{current:n.isCurrent}},[s("lbvs-instance-name",{attrs:{instance:n}}),n.isCurrent?s("span",{staticClass:"lbvs-status-current"},[e._v(" "+e._s(e.$t("versions.label.current"))+" ")]):e._e(),n.version?s("lbvs-version",{attrs:{version:{name:n.version,label:n.versionLabel}}}):e._e()],1)}),0)]),s("k-column",{staticClass:"lbvs-status-changes",attrs:{width:"2/3"}},[s("header",{staticClass:"k-section-header"},[s("k-label",{attrs:{type:"section"}},[e._v(" "+e._s(e.$t("versions.label.changes"))+" ")]),s("k-button",{attrs:{icon:"add",size:"xs",disabled:e.canCreateVersion===!1},on:{click:e.onCreate}},[e._v(" "+e._s(e.$t("versions.button.create"))+" ")])],1),s("lbvs-changes",{attrs:{changes:e.currentChanges}})],1)],1)],1),s("lbvs-create-dialog",{ref:"createDialog"})],1)},se=[],te=i(Z,ee,se,!1,null,null,null,null);const ne=te.exports,re={props:{details:{type:Array,default(){return[]}},instances:Boolean,version:Object},computed:{mergedDetails(){return[{title:this.$t("versions.label.versionName"),value:this.version.name},...this.details].filter(e=>e.value)}}};var ae=function(){var e=this,s=e._self._c;return s("div",{staticClass:"lbvs-version"},[s("strong",[e._v(e._s(e.version.label))]),s("dl",{staticClass:"lbvs-version-details"},[e._l(e.mergedDetails,function(n){return[s("dt",{key:n.title,staticClass:"sr-only"},[e._v(e._s(n.title)+":")]),s("dd",{key:n.title,attrs:{title:n.title}},[e._v(" "+e._s(n.value)+" ")])]})],2)])},ie=[],oe=i(re,ae,ie,!1,null,null,null,null);const le=oe.exports,ce={props:{value:Object}};var ue=function(){var e=this,s=e._self._c;return s("div",{staticClass:"lbvs-version-label-cell"},[s("lbvs-version",{attrs:{version:e.value}})],1)},ve=[],de=i(ce,ue,ve,!1,null,null,null,null);const _e=de.exports,fe={computed:{columns(){return{title:{label:this.$t("versions.label.label"),type:"lbvs-version-label",mobile:!0,width:"35%"},instances:{label:this.$t("versions.label.instances"),type:"lbvs-instance-names",mobile:!0,width:"25%"},creation:{label:this.$t("versions.label.creation"),type:"text",width:"25%"},originInstance:{label:this.$t("versions.label.originInstance"),type:"lbvs-instance-names",width:"15%"}}},items(){return Object.values(this.$store.state.versions.data.versions).map(t=>(t.creation=this.$t("versions.label.creationData",{created:t.created?this.$library.dayjs.unix(t.created).format("YYYY-MM-DD HH:mm"):"?",creator:t.creatorName||t.creatorEmail||"?"}),t.title={label:t.label,name:t.name},t.options=this.options(t),t))}},methods:{onOption(t,e){return this.$refs[t+"Dialog"].open(e)},options(t){let e=this.$permissions["lukasbestle.versions"];return[{click:"export",disabled:e.export!==!0,icon:"download",text:this.$t("versions.button.export")},{click:"deploy",disabled:e.deploy!==!0,icon:"wand",text:this.$t("versions.button.deploy")},{click:"delete",disabled:e.delete!==!0||t.instances.length>0,icon:"trash",text:this.$t("versions.button.delete")}]}}};var he=function(){var e=this,s=e._self._c;return s("div",{staticClass:"lbvs-versions"},[s("header",{staticClass:"k-section-header"},[s("k-label",{attrs:{type:"section"}},[e._v(" "+e._s(e.$t("versions.label.versions"))+" ")])],1),e.items.length?s("k-items",{attrs:{columns:e.columns,items:e.items,layout:"table",sortable:!1},on:{option:e.onOption}}):s("k-empty",{attrs:{icon:"protected",layout:"table"}},[e._v(" "+e._s(e.$t("versions.label.empty"))+" ")]),s("lbvs-export-dialog",{ref:"exportDialog"}),s("lbvs-deploy-dialog",{ref:"deployDialog"}),s("lbvs-delete-dialog",{ref:"deleteDialog"})],1)},be=[],pe=i(fe,he,be,!1,null,null,null,null);const me=pe.exports,ge={data(){return{isLoading:!0}},async mounted(){this.$permissions["lukasbestle.versions"].access!==!0&&this.$go("/");try{this.isLoading=!0,await this.$store.dispatch("versions/load")}finally{this.isLoading=!1}}};var $e=function(){var e=this,s=e._self._c;return s("k-inside",[s("div",{staticClass:"lbvs-view"},[e.isLoading?s("k-loader"):[s("lbvs-status"),s("lbvs-versions")]],2)])},ye=[],Ce=i(ge,$e,ye,!1,null,null,null,null);const ke=Ce.exports,we=t=>({namespaced:!0,state:{data:{instances:{},versions:{}}},getters:{currentInstance(e){return Object.values(e.data.instances).find(s=>s.isCurrent)}},mutations:{SET_DATA(e,{instances:s,versions:n}){e.data.instances=s,e.data.versions=n}},actions:{async load({commit:e}){e("SET_DATA",await t.$api.get("versions"))},async prepareVersionCreation(e,{instance:s}){return await t.$api.post("versions/prepareCreation",{instance:s})},async createVersion({commit:e},{instance:s,label:n}){let r=await t.$api.post("versions/create",{instance:s,label:n});e("SET_DATA",r)},async deleteVersion({commit:e},{version:s}){let n=await t.$api.delete("versions/versions/"+s);e("SET_DATA",n)},async deployVersion({commit:e},{instance:s,version:n}){let r=await t.$api.post("versions/versions/"+n+"/deploy",{instance:s});e("SET_DATA",r)},async exportVersion(e,{version:s}){return await t.$api.post("versions/versions/"+s+"/export")}}});panel.plugin("lukasbestle/versions",{components:{"k-table-lbvs-instance-names-cell":Q,"k-table-lbvs-version-label-cell":_e,"lbvs-changes":p,"lbvs-create-error-dialog":C,"lbvs-create-dialog":S,"lbvs-delete-dialog":O,"lbvs-deploy-dialog":E,"lbvs-export-dialog":Y,"lbvs-instance-name":q,"lbvs-status":ne,"lbvs-version":le,"lbvs-versions":me,"lbvs-view":ke},created(t){t.$store.registerModule("versions",we(t))}})})(); +(function() { + "use strict"; + function normalizeComponent(scriptExports, render, staticRenderFns, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) { + var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports; + if (render) { + options.render = render; + options.staticRenderFns = staticRenderFns; + options._compiled = true; + } + if (functionalTemplate) { + options.functional = true; + } + if (scopeId) { + options._scopeId = "data-v-" + scopeId; + } + var hook; + if (moduleIdentifier) { + hook = function(context) { + context = context || // cached call + this.$vnode && this.$vnode.ssrContext || // stateful + this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; + if (!context && typeof __VUE_SSR_CONTEXT__ !== "undefined") { + context = __VUE_SSR_CONTEXT__; + } + if (injectStyles) { + injectStyles.call(this, context); + } + if (context && context._registeredComponents) { + context._registeredComponents.add(moduleIdentifier); + } + }; + options._ssrRegister = hook; + } else if (injectStyles) { + hook = shadowMode ? function() { + injectStyles.call( + this, + (options.functional ? this.parent : this).$root.$options.shadowRoot + ); + } : injectStyles; + } + if (hook) { + if (options.functional) { + options._injectStyles = hook; + var originalRender = options.render; + options.render = function renderWithStyleInjection(h, context) { + hook.call(context); + return originalRender(h, context); + }; + } else { + var existing = options.beforeCreate; + options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; + } + } + return { + exports: scriptExports, + options + }; + } + const _sfc_main$d = { + data() { + return { + ready: false + }; + }, + props: { + changes: Object + }, + computed: { + markedChangesForStaging() { + return this.$store.getters["versions/currentChanges"]; + } + }, + mounted() { + for (const change in this.changes) { + this.$store.dispatch("versions/setChanges", { + path: change, + status: this.changes[change] + }); + } + this.ready = true; + }, + methods: { + updateChange(path, state, checked) { + this.$store.dispatch("versions/setChanges", { + path, + status: checked ? state : false + }); + } + }, + watch: { + markedChangesForStaging: { + handler(val) { + console.log(val); + }, + deep: true, + immediate: true + } + } + }; + var _sfc_render$d = function render() { + var _vm = this, _c = _vm._self._c; + return _vm.ready ? _c("ul", { staticClass: "lbvs-changes" }, _vm._l(_vm.changes, function(status, path) { + return _c("li", { key: path }, [_c("div", [_c("k-input", { attrs: { "id": path, "name": path, "value": status, "checked": typeof _vm.markedChangesForStaging[path] !== "undefined", "type": "checkbox" }, on: { "input": ($event) => _vm.updateChange(path, status, $event) } })], 1), _c("span", { attrs: { "data-status": status, "title": _vm.$t("versions.label.status." + status) } }, [_vm._v(" " + _vm._s(status) + " ")]), _c("label", { attrs: { "for": path } }, [_vm._v(_vm._s(path))])]); + }), 0) : _vm._e(); + }; + var _sfc_staticRenderFns$d = []; + _sfc_render$d._withStripped = true; + var __component__$d = /* @__PURE__ */ normalizeComponent( + _sfc_main$d, + _sfc_render$d, + _sfc_staticRenderFns$d, + false, + null, + null, + null, + null + ); + __component__$d.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Misc/Changes.vue"; + const Changes = __component__$d.exports; + const _sfc_main$c = { + props: { + toStage: Object + } + }; + var _sfc_render$c = function render() { + var _vm = this, _c = _vm._self._c; + return _c("ul", { staticClass: "lbvs-changes" }, _vm._l(_vm.toStage, function(status, path) { + return _c("li", { key: path }, [_c("span", { attrs: { "data-status": status, "title": _vm.$t("versions.label.status." + status) } }, [_vm._v(" " + _vm._s(status) + " ")]), _vm._v(" " + _vm._s(path) + " ")]); + }), 0); + }; + var _sfc_staticRenderFns$c = []; + _sfc_render$c._withStripped = true; + var __component__$c = /* @__PURE__ */ normalizeComponent( + _sfc_main$c, + _sfc_render$c, + _sfc_staticRenderFns$c, + false, + null, + null, + null, + null + ); + __component__$c.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Misc/Stages.vue"; + const Stages = __component__$c.exports; + const _sfc_main$b = { + data() { + return { + error: { + message: null, + details: { + lockedModels: {} + } + } + }; + }, + methods: { + open(error) { + this.error = error; + this.$refs.dialog.open(); + } + } + }; + var _sfc_render$b = function render() { + var _vm = this, _c = _vm._self._c; + return _c("k-dialog", { ref: "dialog", staticClass: "lbvs-create-error-dialog", attrs: { "cancel-button": _vm.$t("close"), "submit-button": false } }, [_c("p", { staticClass: "lbvs-create-error-dialog-message" }, [_vm._v(" " + _vm._s(_vm.error.message) + " ")]), _c("ul", { staticClass: "lbvs-create-error-dialog-list" }, _vm._l(_vm.error.details.lockedModels, function(users, model) { + return _c("li", { key: model }, [_vm._v(" " + _vm._s(model) + " "), _c("span", [_vm._v("(" + _vm._s(users.join(", ")) + ")")])]); + }), 0)]); + }; + var _sfc_staticRenderFns$b = []; + _sfc_render$b._withStripped = true; + var __component__$b = /* @__PURE__ */ normalizeComponent( + _sfc_main$b, + _sfc_render$b, + _sfc_staticRenderFns$b, + false, + null, + null, + null, + null + ); + __component__$b.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Dialogs/CreateErrorDialog.vue"; + const CreateErrorDialog = __component__$b.exports; + const _sfc_main$a = { + data() { + return { + instance: null, + inProgress: false, + toStage: {} + }; + }, + computed: { + fields() { + return { + label: { + autofocus: true, + icon: "title", + label: this.$t("versions.label.label"), + type: "text" + } + }; + } + }, + methods: { + async onSubmit() { + if (this.inProgress === true) { + return; + } + try { + this.inProgress = true; + let label = this.$refs.form.value.label; + if (!label) { + throw this.$t("field.required"); + } + await this.$store.dispatch({ + type: "versions/addChangesToStage", + changes: this.toStage + }); + await this.$store.dispatch({ + type: "versions/createVersion", + instance: this.instance, + label + }); + this.$store.dispatch("notification/success", ":)"); + this.$refs.dialog.close(); + } catch (e) { + this.$refs.dialog.error(e.message || e); + } finally { + this.inProgress = false; + } + }, + open(instance, toStage) { + this.instance = instance; + this.toStage = toStage; + this.$refs.dialog.open(); + } + } + }; + var _sfc_render$a = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", [_c("k-dialog", { ref: "dialog", attrs: { "size": "large", "submit-button": _vm.$t("versions.button.create"), "theme": "positive" }, on: { "submit": _vm.onSubmit } }, [_c("k-form", { ref: "form", attrs: { "fields": _vm.fields }, on: { "submit": _vm.onSubmit }, scopedSlots: _vm._u([{ key: "header", fn: function() { + return [_c("k-field", { staticClass: "lbvs-create-changes", attrs: { "label": _vm.$t("versions.label.changes") } }, [_c("lbvs-stages", { attrs: { "to-stage": _vm.toStage } })], 1)]; + }, proxy: true }]) })], 1), _c("lbvs-create-error-dialog", { ref: "errorDialog" })], 1); + }; + var _sfc_staticRenderFns$a = []; + _sfc_render$a._withStripped = true; + var __component__$a = /* @__PURE__ */ normalizeComponent( + _sfc_main$a, + _sfc_render$a, + _sfc_staticRenderFns$a, + false, + null, + null, + null, + null + ); + __component__$a.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Dialogs/CreateDialog.vue"; + const CreateDialog = __component__$a.exports; + const _sfc_main$9 = { + data() { + return { + inProgress: false, + version: null + }; + }, + methods: { + async onSubmit() { + if (this.inProgress === true) { + return; + } + try { + this.inProgress = true; + await this.$store.dispatch({ + type: "versions/deleteVersion", + version: this.version.name + }); + this.$store.dispatch("notification/success", ":)"); + this.$refs.dialog.close(); + } catch (e) { + this.$refs.dialog.error(e.message || e); + } finally { + this.inProgress = false; + } + }, + open(version) { + this.version = version; + this.$refs.dialog.open(); + } + } + }; + var _sfc_render$9 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("k-dialog", { ref: "dialog", staticClass: "lbvs-version-dialog lbvs-version-delete-dialog", attrs: { "icon": "trash", "submit-button": _vm.$t("versions.button.delete"), "theme": "negative" }, on: { "submit": _vm.onSubmit } }, [_vm.version ? _c("lbvs-version", { attrs: { "version": _vm.version } }) : _vm._e(), _c("p", [_vm._v(_vm._s(_vm.$t("versions.message.delete")))])], 1); + }; + var _sfc_staticRenderFns$9 = []; + _sfc_render$9._withStripped = true; + var __component__$9 = /* @__PURE__ */ normalizeComponent( + _sfc_main$9, + _sfc_render$9, + _sfc_staticRenderFns$9, + false, + null, + null, + null, + null + ); + __component__$9.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Dialogs/DeleteDialog.vue"; + const DeleteDialog = __component__$9.exports; + const _sfc_main$8 = { + data() { + return { + inProgress: false, + version: null + }; + }, + computed: { + fields() { + let currentInstance = this.$store.getters["versions/currentInstance"]; + let options = Object.values( + this.$store.state.versions.data.instances + ).map((instance) => ({ text: instance.name, value: instance.name })); + return { + instance: { + disabled: options.length <= 1, + empty: false, + icon: "box", + label: this.$t("versions.label.targetInstance"), + options, + placeholder: currentInstance.name, + type: "select", + value: currentInstance.name + } + }; + } + }, + methods: { + async onSubmit() { + if (this.inProgress === true) { + return; + } + try { + this.inProgress = true; + let instance = this.$refs.form.value.instance || this.$store.getters["versions/currentInstance"].name; + await this.$store.dispatch({ + type: "versions/deployVersion", + version: this.version.name, + instance + }); + this.$store.dispatch("notification/success", ":)"); + this.$refs.dialog.close(); + } catch (e) { + this.$refs.dialog.error(e.message || e); + } finally { + this.inProgress = false; + } + }, + open(version) { + this.version = version; + this.$refs.dialog.open(); + } + } + }; + var _sfc_render$8 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("k-dialog", { ref: "dialog", staticClass: "lbvs-version-dialog", attrs: { "submit-button": _vm.$t("versions.button.deploy"), "theme": "positive" }, on: { "submit": _vm.onSubmit } }, [_vm.version ? _c("lbvs-version", { attrs: { "version": _vm.version } }) : _vm._e(), _c("k-form", { ref: "form", attrs: { "fields": _vm.fields }, on: { "submit": _vm.onSubmit } })], 1); + }; + var _sfc_staticRenderFns$8 = []; + _sfc_render$8._withStripped = true; + var __component__$8 = /* @__PURE__ */ normalizeComponent( + _sfc_main$8, + _sfc_render$8, + _sfc_staticRenderFns$8, + false, + null, + null, + null, + null + ); + __component__$8.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Dialogs/DeployDialog.vue"; + const DeployDialog = __component__$8.exports; + const _sfc_main$7 = { + data() { + return { + data: null, + version: {} + }; + }, + computed: { + details() { + if (this.data) { + return [ + { + title: this.$t("versions.label.fileSize"), + value: this.data.filesize + } + ]; + } + return []; + }, + supportsClipboard() { + try { + window.navigator.clipboard.writeText; + return true; + } catch (e) { + return false; + } + } + }, + methods: { + async copyToClipboard() { + await window.navigator.clipboard.writeText(this.data.url); + this.$store.dispatch("notification/success", ":)"); + }, + download() { + window.location = this.data.url; + this.$store.dispatch("notification/success", ":)"); + }, + async open(version) { + this.data = null; + this.version = version; + this.$refs.dialog.open(); + let data = await this.$store.dispatch({ + type: "versions/exportVersion", + version: this.version.name + }); + if (version === this.version) { + this.data = data; + } + } + } + }; + var _sfc_render$7 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("k-dialog", { ref: "dialog", staticClass: "lbvs-version-dialog", attrs: { "cancel-button": _vm.$t(_vm.data ? "close" : "cancel"), "submit-button": false } }, [_vm.version ? _c("lbvs-version", { attrs: { "details": _vm.details, "version": _vm.version } }) : _vm._e(), !_vm.data ? _c("p", [_vm._v(" " + _vm._s(_vm.$t("versions.message.exporting")) + " ")]) : _c("k-button-group", [_c("k-button", { attrs: { "icon": "download" }, on: { "click": _vm.download } }, [_vm._v(" " + _vm._s(_vm.$t("versions.button.download")) + " ")]), _c("k-button", { attrs: { "icon": "copy", "disabled": !_vm.supportsClipboard }, on: { "click": _vm.copyToClipboard } }, [_vm._v(" " + _vm._s(_vm.$t("versions.button.copyLink")) + " ")])], 1)], 1); + }; + var _sfc_staticRenderFns$7 = []; + _sfc_render$7._withStripped = true; + var __component__$7 = /* @__PURE__ */ normalizeComponent( + _sfc_main$7, + _sfc_render$7, + _sfc_staticRenderFns$7, + false, + null, + null, + null, + null + ); + __component__$7.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Dialogs/ExportDialog.vue"; + const ExportDialog = __component__$7.exports; + const _sfc_main$6 = { + props: { + inline: Boolean, + // support both a full instance object or just the name string + instance: [Object, String] + }, + computed: { + element() { + return this.inline ? "span" : "strong"; + }, + instanceObj() { + if (typeof this.instance === "string") { + return { name: this.instance, color: "var(--color-gray-300)" }; + } + return this.instance; + } + } + }; + var _sfc_render$6 = function render() { + var _vm = this, _c = _vm._self._c; + return _c(_vm.element, { tag: "component", staticClass: "lbvs-instance-name", style: { backgroundColor: _vm.instanceObj.color } }, [_vm._v(" " + _vm._s(_vm.instanceObj.name) + " ")]); + }; + var _sfc_staticRenderFns$6 = []; + _sfc_render$6._withStripped = true; + var __component__$6 = /* @__PURE__ */ normalizeComponent( + _sfc_main$6, + _sfc_render$6, + _sfc_staticRenderFns$6, + false, + null, + null, + null, + null + ); + __component__$6.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Misc/InstanceName.vue"; + const InstanceName = __component__$6.exports; + const _sfc_main$5 = { + props: { + // support both a list of instances or a single instance + value: [Array, String] + }, + computed: { + instances() { + return Array.isArray(this.value) ? this.value : [this.value]; + } + } + }; + var _sfc_render$5 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", { staticClass: "lbvs-instance-names-cell" }, _vm._l(_vm.instances, function(instance) { + return _c("lbvs-instance-name", { key: instance, attrs: { "inline": true, "instance": _vm.$store.state.versions.data.instances[instance] || instance } }); + }), 1); + }; + var _sfc_staticRenderFns$5 = []; + _sfc_render$5._withStripped = true; + var __component__$5 = /* @__PURE__ */ normalizeComponent( + _sfc_main$5, + _sfc_render$5, + _sfc_staticRenderFns$5, + false, + null, + null, + null, + null + ); + __component__$5.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Cells/InstanceNames.vue"; + const InstanceNamesCell = __component__$5.exports; + const _sfc_main$4 = { + emits: ["uncheck"], + computed: { + canCreateVersion() { + return this.$permissions["lukasbestle.versions"].create === true && Object.keys(this.markedChanges).length > 0; + }, + canCheck() { + return this.$permissions["lukasbestle.versions"].create === true && Object.keys(this.markedChanges).length > 0; + }, + canUncheck() { + return this.$permissions["lukasbestle.versions"].create === true && !Object.keys(this.markedChanges).length > 0; + }, + currentChanges() { + return this.$store.getters["versions/currentInstance"].changes; + }, + markedChanges() { + return this.$store.getters["versions/currentChanges"]; + } + }, + methods: { + onUncheck() { + this.$store.dispatch("versions/uncheckAll"); + }, + onCheckall() { + this.$store.dispatch("versions/checkAll"); + }, + onCreate() { + let instance = this.$store.getters["versions/currentInstance"].name; + const toStage = this.$store.getters["versions/currentChanges"]; + return this.$refs.createDialog.open(instance, toStage); + } + } + }; + var _sfc_render$4 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", { staticClass: "lbvs-status" }, [_c("k-view", [_c("k-grid", [_c("k-column", { attrs: { "width": "1/3" } }, [_c("header", { staticClass: "k-section-header" }, [_c("k-label", { attrs: { "type": "section" } }, [_vm._v(" " + _vm._s(_vm.$t("versions.label.instances")) + " ")])], 1), _c("ul", { staticClass: "lbvs-status-instances" }, _vm._l(_vm.$store.state.versions.data.instances, function(instance) { + return _c("li", { key: instance.name, class: { current: instance.isCurrent } }, [_c("lbvs-instance-name", { attrs: { "instance": instance } }), instance.isCurrent ? _c("span", { staticClass: "lbvs-status-current" }, [_vm._v(" " + _vm._s(_vm.$t("versions.label.current")) + " ")]) : _vm._e(), instance.version ? _c("lbvs-version", { attrs: { "version": { + name: instance.version, + label: instance.versionLabel + } } }) : _vm._e()], 1); + }), 0)]), _c("k-column", { staticClass: "lbvs-status-changes", attrs: { "width": "2/3" } }, [_c("header", { staticClass: "k-section-header" }, [_c("k-label", { attrs: { "type": "section" } }, [_vm._v(" " + _vm._s(_vm.$t("versions.label.changes")) + " ")]), _vm.canCheck ? _c("k-button", { attrs: { "icon": "undo", "size": "xs", "disabled": _vm.canCheck === false }, on: { "click": _vm.onUncheck } }, [_vm._v(" " + _vm._s(_vm.$t("versions.button.uncheckall")) + " ")]) : _vm._e(), _vm.canUncheck ? _c("k-button", { attrs: { "icon": "wand", "size": "xs", "disabled": _vm.canUncheck === false }, on: { "click": _vm.onCheckall } }, [_vm._v(" " + _vm._s(_vm.$t("versions.button.checkall")) + " ")]) : _vm._e(), _c("k-button", { attrs: { "icon": "add", "size": "xs", "disabled": _vm.canCreateVersion === false }, on: { "click": _vm.onCreate } }, [_vm._v(" " + _vm._s(_vm.$t("versions.button.create")) + " ")])], 1), _c("lbvs-changes", { attrs: { "changes": _vm.currentChanges } })], 1)], 1)], 1), _c("lbvs-create-dialog", { ref: "createDialog" })], 1); + }; + var _sfc_staticRenderFns$4 = []; + _sfc_render$4._withStripped = true; + var __component__$4 = /* @__PURE__ */ normalizeComponent( + _sfc_main$4, + _sfc_render$4, + _sfc_staticRenderFns$4, + false, + null, + null, + null, + null + ); + __component__$4.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Status.vue"; + const Status = __component__$4.exports; + const _sfc_main$3 = { + props: { + details: { + type: Array, + default() { + return []; + } + }, + instances: Boolean, + version: Object + }, + computed: { + mergedDetails() { + let details = [ + { + title: this.$t("versions.label.versionName"), + value: this.version.name + }, + ...this.details + ]; + return details.filter((detail) => detail.value); + } + } + }; + var _sfc_render$3 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", { staticClass: "lbvs-version" }, [_c("strong", [_vm._v(_vm._s(_vm.version.label))]), _c("dl", { staticClass: "lbvs-version-details" }, [_vm._l(_vm.mergedDetails, function(detail) { + return [_c("dt", { key: detail.title, staticClass: "sr-only" }, [_vm._v(_vm._s(detail.title) + ":")]), _c("dd", { key: detail.title, attrs: { "title": detail.title } }, [_vm._v(" " + _vm._s(detail.value) + " ")])]; + })], 2)]); + }; + var _sfc_staticRenderFns$3 = []; + _sfc_render$3._withStripped = true; + var __component__$3 = /* @__PURE__ */ normalizeComponent( + _sfc_main$3, + _sfc_render$3, + _sfc_staticRenderFns$3, + false, + null, + null, + null, + null + ); + __component__$3.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Misc/Version.vue"; + const Version = __component__$3.exports; + const _sfc_main$2 = { + props: { + value: Object + } + }; + var _sfc_render$2 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", { staticClass: "lbvs-version-label-cell" }, [_c("lbvs-version", { attrs: { "version": _vm.value } })], 1); + }; + var _sfc_staticRenderFns$2 = []; + _sfc_render$2._withStripped = true; + var __component__$2 = /* @__PURE__ */ normalizeComponent( + _sfc_main$2, + _sfc_render$2, + _sfc_staticRenderFns$2, + false, + null, + null, + null, + null + ); + __component__$2.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Cells/VersionLabel.vue"; + const VersionLabelCell = __component__$2.exports; + const _sfc_main$1 = { + computed: { + columns() { + return { + title: { + label: this.$t("versions.label.label"), + type: "lbvs-version-label", + mobile: true, + width: "35%" + }, + instances: { + label: this.$t("versions.label.instances"), + type: "lbvs-instance-names", + mobile: true, + width: "25%" + }, + creation: { + label: this.$t("versions.label.creation"), + type: "text", + width: "25%" + }, + originInstance: { + label: this.$t("versions.label.originInstance"), + type: "lbvs-instance-names", + width: "15%" + } + }; + }, + items() { + return Object.values(this.$store.state.versions.data.versions).map( + (version) => { + version.creation = this.$t("versions.label.creationData", { + created: version.created ? this.$library.dayjs.unix(version.created).format("YYYY-MM-DD HH:mm") : "?", + creator: version.creatorName || version.creatorEmail || "?" + }); + version.title = { + label: version.label, + name: version.name + }; + version.options = this.options(version); + return version; + } + ); + } + }, + methods: { + onOption(option, version) { + return this.$refs[option + "Dialog"].open(version); + }, + options(version) { + let permissions = this.$permissions["lukasbestle.versions"]; + return [ + { + click: "export", + disabled: permissions.export !== true, + icon: "download", + text: this.$t("versions.button.export") + }, + { + click: "deploy", + disabled: permissions.deploy !== true, + icon: "wand", + text: this.$t("versions.button.deploy") + }, + { + click: "delete", + disabled: permissions.delete !== true || version.instances.length > 0, + icon: "trash", + text: this.$t("versions.button.delete") + } + ]; + } + } + }; + var _sfc_render$1 = function render() { + var _vm = this, _c = _vm._self._c; + return _c("div", { staticClass: "lbvs-versions" }, [_c("header", { staticClass: "k-section-header" }, [_c("k-label", { attrs: { "type": "section" } }, [_vm._v(" " + _vm._s(_vm.$t("versions.label.versions")) + " ")])], 1), _vm.items.length ? _c("k-items", { attrs: { "columns": _vm.columns, "items": _vm.items, "layout": "table", "sortable": false }, on: { "option": _vm.onOption } }) : _c("k-empty", { attrs: { "icon": "protected", "layout": "table" } }, [_vm._v(" " + _vm._s(_vm.$t("versions.label.empty")) + " ")]), _c("lbvs-export-dialog", { ref: "exportDialog" }), _c("lbvs-deploy-dialog", { ref: "deployDialog" }), _c("lbvs-delete-dialog", { ref: "deleteDialog" })], 1); + }; + var _sfc_staticRenderFns$1 = []; + _sfc_render$1._withStripped = true; + var __component__$1 = /* @__PURE__ */ normalizeComponent( + _sfc_main$1, + _sfc_render$1, + _sfc_staticRenderFns$1, + false, + null, + null, + null, + null + ); + __component__$1.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/Versions.vue"; + const Versions = __component__$1.exports; + const _sfc_main = { + data() { + return { + isLoading: true + }; + }, + async mounted() { + if (this.$permissions["lukasbestle.versions"].access !== true) { + this.$go("/"); + } + try { + this.isLoading = true; + await this.$store.dispatch("versions/load"); + } finally { + this.isLoading = false; + } + } + }; + var _sfc_render = function render() { + var _vm = this, _c = _vm._self._c; + return _c("k-inside", [_c("div", { staticClass: "lbvs-view" }, [_vm.isLoading ? _c("k-loader") : [_c("lbvs-status"), _c("lbvs-versions")]], 2)]); + }; + var _sfc_staticRenderFns = []; + _sfc_render._withStripped = true; + var __component__ = /* @__PURE__ */ normalizeComponent( + _sfc_main, + _sfc_render, + _sfc_staticRenderFns, + false, + null, + null, + null, + null + ); + __component__.options.__file = "/Users/marcel/Code/packages/kirby-versions/src/frontend/components/View.vue"; + const View = __component__.exports; + const Store = (app) => ({ + namespaced: true, + state: { + data: { + markedChangesForStaging: {}, + instances: {}, + versions: {} + } + }, + getters: { + /** + * @returns {object} The instance marked with `isCurrent: true` + */ + currentInstance(state) { + return Object.values(state.data.instances).find( + (instance) => instance.isCurrent + ); + }, + currentChanges(state) { + return state.data.markedChangesForStaging; + } + }, + mutations: { + SET_DATA(state, { instances, versions }) { + state.data.instances = instances; + state.data.versions = versions; + }, + SET_CHANGES(state, { path, status }) { + Vue.set(state.data.markedChangesForStaging, path, status); + }, + UNCHECK_ALL(state) { + Vue.set(state.data, "markedChangesForStaging", {}); + }, + CHECK_ALL(state) { + const changes = Object.values(state.data.instances).find( + (instance) => instance.isCurrent + ).changes; + Vue.set(state.data, "markedChangesForStaging", changes); + }, + REMOVE_CHANGES(state, { path }) { + Vue.delete(state.data.markedChangesForStaging, path); + } + }, + actions: { + /** + * Initialize the plugin data from the API + */ + async load({ commit }) { + commit("SET_DATA", await app.$api.get("versions")); + }, + /** + * Prepare version creation + * Stages all changes and validates that the version + * can be created based on the current set of changes + * + * @param {string} instance Name of the instance to create the version from + * @returns {object} List of staged changes + */ + async prepareVersionCreation(context, { instance }) { + return await app.$api.post("versions/prepareCreation", { instance }); + }, + /** + * Stage changes + * Stage changes for version creation + * + * @param {object} changes List of changes to stage + */ + async addChangesToStage({ commit }, { changes }) { + await app.$api.post("versions/addChanges", { changes }); + }, + /** + * Create version + * Commits the previously prepared version + * + * @param {string} instance Name of the instance to create the version from + * @param {string} label Custom version label + */ + async createVersion({ commit }, { instance, label }) { + let data = await app.$api.post("versions/create", { instance, label }); + commit("SET_DATA", data); + }, + /** + * Delete version + * Deletes a version's Git tag + * + * @param {string} version Unique version name + */ + async deleteVersion({ commit }, { version }) { + let data = await app.$api.delete("versions/versions/" + version); + commit("SET_DATA", data); + }, + /** + * Deploy version + * Deploys a version to a specified instance + * + * @param {string} instance Name of the instance to deploy to + * @param {string} version Unique version name + */ + async deployVersion({ commit }, { instance, version }) { + let data = await app.$api.post( + "versions/versions/" + version + "/deploy", + { instance } + ); + commit("SET_DATA", data); + }, + /** + * Export version + * Returns the URL to a ZIP file of the given version + * + * @param {string} version Unique version name + * @returns {object} ZIP `url`, version `name` and `label`, `filesize` + */ + async exportVersion(context, { version }) { + return await app.$api.post("versions/versions/" + version + "/export"); + }, + setChanges({ commit }, { path, status }) { + if (status === false) { + commit("REMOVE_CHANGES", { path }); + return; + } + return commit("SET_CHANGES", { path, status }); + }, + uncheckAll({ commit }) { + commit("UNCHECK_ALL"); + }, + checkAll({ commit }) { + commit("CHECK_ALL"); + } + } + }); + panel.plugin("lukasbestle/versions", { + components: { + "k-table-lbvs-instance-names-cell": InstanceNamesCell, + "k-table-lbvs-version-label-cell": VersionLabelCell, + "lbvs-changes": Changes, + "lbvs-stages": Stages, + "lbvs-create-error-dialog": CreateErrorDialog, + "lbvs-create-dialog": CreateDialog, + "lbvs-delete-dialog": DeleteDialog, + "lbvs-deploy-dialog": DeployDialog, + "lbvs-export-dialog": ExportDialog, + "lbvs-instance-name": InstanceName, + "lbvs-status": Status, + "lbvs-version": Version, + "lbvs-versions": Versions, + "lbvs-view": View + }, + created(app) { + app.$store.registerModule("versions", Store(app)); + } + }); +})(); diff --git a/package-lock.json b/package-lock.json index b141a27..4bb1dc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "versions", + "name": "kirby-versions", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/src/classes/Plugin.php b/src/classes/Plugin.php index 123021c..3bd1128 100644 --- a/src/classes/Plugin.php +++ b/src/classes/Plugin.php @@ -244,6 +244,25 @@ public function option(string $key) return $this->kirby()->option('lukasbestle.versions.' . $key); } + /** + * Stage list of changes + * + * @param $list + * + * @return bool + */ + public function stageChanges(array $list): bool + { + try { + // First reset all stages changes + $this->gitCommand(null, 'reset'); + $this->gitCommand(null, 'add', ...array_keys($list)); + } catch (Exception $e) { + return false; + } + return true; + } + /** * Returns the plugin state as array filtered * to the necessary data for the API diff --git a/src/config/api.php b/src/config/api.php index d80540e..43ff0d4 100644 --- a/src/config/api.php +++ b/src/config/api.php @@ -44,6 +44,26 @@ return $instance->changes()->inIndex(); } ], + [ + /** + * Stage changes + * Stages all marked changes and validates that the version + * can be created based on the current set of changes + * + * @param string body:list of files to stage + * + * @return bool if staging was successful + */ + 'pattern' => 'versions/addChanges', + 'method' => 'POST', + 'action' => function (): bool { + /** @psalm-scope-this Kirby\Http\Route */ + $plugin = Plugin::instance($this->kirby()); + $plugin->checkPermission('create'); + + return $plugin->stageChanges($this->requestBody('changes')); + } + ], [ /** * Create version diff --git a/src/config/i18n/de.php b/src/config/i18n/de.php index ead1e7f..62da110 100644 --- a/src/config/i18n/de.php +++ b/src/config/i18n/de.php @@ -19,6 +19,8 @@ 'versions.button.copyLink' => 'Link kopieren', 'versions.button.create' => 'Version erstellen', + 'versions.button.uncheckall' => 'Alles abwählen', + 'versions.button.checkall' => 'Alles wählen', 'versions.button.delete' => 'Löschen', 'versions.button.deploy' => 'Verwenden', 'versions.button.download' => 'Herunterladen', diff --git a/src/config/i18n/en.php b/src/config/i18n/en.php index b4f28da..1a2792d 100644 --- a/src/config/i18n/en.php +++ b/src/config/i18n/en.php @@ -19,6 +19,8 @@ 'versions.button.copyLink' => 'Copy link', 'versions.button.create' => 'Create version', + 'versions.button.uncheckall' => 'Uncheck all', + 'versions.button.checkall' => 'Check all', 'versions.button.delete' => 'Delete', 'versions.button.deploy' => 'Deploy', 'versions.button.download' => 'Download', diff --git a/src/config/i18n/fr.php b/src/config/i18n/fr.php index 16d0bdd..88d9ff9 100644 --- a/src/config/i18n/fr.php +++ b/src/config/i18n/fr.php @@ -19,6 +19,8 @@ 'versions.button.copyLink' => 'Copier le lien', 'versions.button.create' => 'Créer une version', + 'versions.button.uncheckall' => 'Tout décocher', + 'versions.button.checkall' => 'Tout cocher', 'versions.button.delete' => 'Supprimer', 'versions.button.deploy' => 'Déployer', 'versions.button.download' => 'Télécharger', diff --git a/src/frontend/components/Dialogs/CreateDialog.vue b/src/frontend/components/Dialogs/CreateDialog.vue index dae2d62..78a76bb 100644 --- a/src/frontend/components/Dialogs/CreateDialog.vue +++ b/src/frontend/components/Dialogs/CreateDialog.vue @@ -13,7 +13,7 @@ :label="$t('versions.label.changes')" class="lbvs-create-changes" > - + @@ -29,7 +29,7 @@ export default { return { instance: null, inProgress: false, - stagedChanges: {} + toStage: {} }; }, computed: { @@ -59,6 +59,12 @@ export default { throw this.$t("field.required"); } + // stage list of changes + await this.$store.dispatch({ + type: "versions/addChangesToStage", + changes: this.toStage + }); + await this.$store.dispatch({ type: "versions/createVersion", instance: this.instance, @@ -73,21 +79,9 @@ export default { this.inProgress = false; } }, - async open(instance) { + open(instance, toStage) { this.instance = instance; - - try { - this.stagedChanges = await this.$store.dispatch({ - type: "versions/prepareVersionCreation", - instance: this.instance - }); - } catch (e) { - if (e.key === "error.versions.lockFiles") { - return this.$refs.errorDialog.open(e); - } - - throw e; - } + this.toStage = toStage; this.$refs.dialog.open(); } diff --git a/src/frontend/components/Misc/Changes.vue b/src/frontend/components/Misc/Changes.vue index 1ab07f2..ce4b2d8 100644 --- a/src/frontend/components/Misc/Changes.vue +++ b/src/frontend/components/Misc/Changes.vue @@ -1,21 +1,66 @@ - + + + updateChange(path, status, $event)"/> + {{ status }} - {{ path }} + {{ path }} @@ -24,22 +69,26 @@ export default { .lbvs-changes li { padding-inline-start: 1.2em; position: relative; + display: flex; } -.lbvs-changes li span { +.lbvs-changes li > span { position: absolute; inset-inline-start: 0; font-family: var(--font-mono); font-weight: bold; } + .lbvs-changes li span[data-status="+"], .lbvs-changes li span[data-status="C"] { color: var(--color-positive); } + .lbvs-changes li span[data-status="-"] { color: var(--color-negative); } + .lbvs-changes li span[data-status="M"], .lbvs-changes li span[data-status="R"] { color: var(--color-notice); diff --git a/src/frontend/components/Misc/Stages.vue b/src/frontend/components/Misc/Stages.vue new file mode 100644 index 0000000..5aaace5 --- /dev/null +++ b/src/frontend/components/Misc/Stages.vue @@ -0,0 +1,72 @@ + + + + + {{ status }} + + {{ path }} + + + + + + + diff --git a/src/frontend/components/Status.vue b/src/frontend/components/Status.vue index 471a657..c089f1c 100644 --- a/src/frontend/components/Status.vue +++ b/src/frontend/components/Status.vue @@ -38,6 +38,26 @@ {{ $t("versions.label.changes") }} + + {{ $t("versions.button.uncheckall") }} + + + + {{ $t("versions.button.checkall") }} + + export default { + emits: ['uncheck'], computed: { canCreateVersion() { // user must have the create permission and // there needs to be at least one change to commit return ( this.$permissions["lukasbestle.versions"].create === true && - Object.keys(this.currentChanges).length > 0 + Object.keys(this.markedChanges).length > 0 + ); + }, + canCheck() { + // user must have the create permission and + // there needs to be at least one change to commit + return ( + this.$permissions["lukasbestle.versions"].create === true && + Object.keys(this.markedChanges).length > 0 + ); + }, + canUncheck() { + // user must have the create permission and + // there needs to be at least one change to commit + return ( + this.$permissions["lukasbestle.versions"].create === true && + !Object.keys(this.markedChanges).length > 0 ); }, currentChanges() { return this.$store.getters["versions/currentInstance"].changes; + }, + markedChanges() { + return this.$store.getters["versions/currentChanges"]; } }, methods: { + onUncheck() { + this.$store.dispatch("versions/uncheckAll"); + }, + onCheckall() { + this.$store.dispatch("versions/checkAll"); + }, onCreate() { let instance = this.$store.getters["versions/currentInstance"].name; - return this.$refs.createDialog.open(instance); + const toStage = this.$store.getters["versions/currentChanges"]; + return this.$refs.createDialog.open(instance, toStage); } } }; diff --git a/src/frontend/index.js b/src/frontend/index.js index eb5979a..3840452 100644 --- a/src/frontend/index.js +++ b/src/frontend/index.js @@ -1,5 +1,6 @@ // Vue components import Changes from "./components/Misc/Changes.vue"; +import Stages from "./components/Misc/Stages.vue"; import CreateErrorDialog from "./components/Dialogs/CreateErrorDialog.vue"; import CreateDialog from "./components/Dialogs/CreateDialog.vue"; import DeleteDialog from "./components/Dialogs/DeleteDialog.vue"; @@ -21,6 +22,7 @@ panel.plugin("lukasbestle/versions", { "k-table-lbvs-instance-names-cell": InstanceNamesCell, "k-table-lbvs-version-label-cell": VersionLabelCell, "lbvs-changes": Changes, + "lbvs-stages": Stages, "lbvs-create-error-dialog": CreateErrorDialog, "lbvs-create-dialog": CreateDialog, "lbvs-delete-dialog": DeleteDialog, diff --git a/src/frontend/store.js b/src/frontend/store.js index 58b4c8d..b6558c8 100644 --- a/src/frontend/store.js +++ b/src/frontend/store.js @@ -2,6 +2,7 @@ export default (app) => ({ namespaced: true, state: { data: { + markedChangesForStaging: {}, instances: {}, versions: {} } @@ -14,14 +15,39 @@ export default (app) => ({ return Object.values(state.data.instances).find( (instance) => instance.isCurrent ); + }, + + currentChanges(state) { + return state.data.markedChangesForStaging; } }, mutations: { SET_DATA(state, { instances, versions }) { state.data.instances = instances; state.data.versions = versions; + }, + + SET_CHANGES(state, { path, status }) { + Vue.set(state.data.markedChangesForStaging, path, status) + }, + + UNCHECK_ALL(state) { + Vue.set(state.data, 'markedChangesForStaging', {}) + }, + + CHECK_ALL(state) { + const changes = Object.values(state.data.instances).find( + (instance) => instance.isCurrent + ).changes; + + Vue.set(state.data, 'markedChangesForStaging', changes) + }, + + REMOVE_CHANGES(state, { path }) { + Vue.delete(state.data.markedChangesForStaging, path); } }, + actions: { /** * Initialize the plugin data from the API @@ -42,6 +68,16 @@ export default (app) => ({ return await app.$api.post("versions/prepareCreation", { instance }); }, + /** + * Stage changes + * Stage changes for version creation + * + * @param {object} changes List of changes to stage + */ + async addChangesToStage({ commit }, { changes }) { + await app.$api.post("versions/addChanges", { changes }); + }, + /** * Create version * Commits the previously prepared version @@ -89,6 +125,22 @@ export default (app) => ({ */ async exportVersion(context, { version }) { return await app.$api.post("versions/versions/" + version + "/export"); + }, + + setChanges({ commit }, { path, status }) { + if (status === false) { + commit("REMOVE_CHANGES", { path }); + return; + } + return commit("SET_CHANGES", { path, status }); + }, + + uncheckAll({ commit }) { + commit("UNCHECK_ALL"); + }, + + checkAll({ commit }) { + commit("CHECK_ALL"); } } });