feat: upgrade Filament to v5.2.1
This commit is contained in:
parent
a30be84084
commit
ca4b9b6138
@ -36,6 +36,7 @@
|
||||
use Filament\Tables\Contracts\HasTable;
|
||||
use Filament\Tables\Filters\TrashedFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use UnitEnum;
|
||||
|
||||
@ -81,6 +82,17 @@ public static function canCreate(): bool
|
||||
&& $resolver->can($user, $tenant, Capabilities::TENANT_SYNC);
|
||||
}
|
||||
|
||||
public static function getEloquentQuery(): Builder
|
||||
{
|
||||
$tenant = Filament::getTenant();
|
||||
|
||||
if (! $tenant instanceof Tenant) {
|
||||
return parent::getEloquentQuery()->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
return parent::getEloquentQuery()->where('tenant_id', (int) $tenant->getKey());
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"filament/filament": "^5.0",
|
||||
"filament/filament": "^5.2.1",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"torchlight/engine": "^0.1.0",
|
||||
|
||||
455
composer.lock
generated
455
composer.lock
generated
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(()=>{var n=({livewireId:e})=>({actionNestingIndex:null,init(){window.addEventListener("sync-action-modals",t=>{t.detail.id===e&&this.syncActionModals(t.detail.newActionNestingIndex)})},syncActionModals(t){if(this.actionNestingIndex===t){this.actionNestingIndex!==null&&this.$nextTick(()=>this.openModal());return}if(this.actionNestingIndex!==null&&this.closeModal(),this.actionNestingIndex=t,this.actionNestingIndex!==null){if(!this.$el.querySelector(`#${this.generateModalId(t)}`)){this.$nextTick(()=>this.openModal());return}this.openModal()}},generateModalId(t){return`fi-${e}-action-`+t},openModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("open-modal",{bubbles:!0,composed:!0,detail:{id:t}}))},closeModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("close-modal-quietly",{bubbles:!0,composed:!0,detail:{id:t}}))}});document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentActionModals",n)});})();
|
||||
(()=>{var n=({livewireId:e})=>({actionNestingIndex:null,init(){window.addEventListener("sync-action-modals",t=>{t.detail.id===e&&this.syncActionModals(t.detail.newActionNestingIndex,t.detail.shouldOverlayParentActions??!1)})},syncActionModals(t,i=!1){if(this.actionNestingIndex===t){this.actionNestingIndex!==null&&this.$nextTick(()=>this.openModal());return}let s=this.actionNestingIndex!==null&&t!==null&&t>this.actionNestingIndex;if(this.actionNestingIndex!==null&&!(i&&s)&&this.closeModal(),this.actionNestingIndex=t,this.actionNestingIndex!==null){if(!this.$el.querySelector(`#${this.generateModalId(t)}`)){this.$nextTick(()=>this.openModal());return}this.openModal()}},generateModalId(t){return`fi-${e}-action-`+t},openModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("open-modal",{bubbles:!0,composed:!0,detail:{id:t}}))},closeModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("close-modal-quietly",{bubbles:!0,composed:!0,detail:{id:t}}))}});document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentActionModals",n)});})();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
function c({livewireId:s}){return{areAllCheckboxesChecked:!1,checkboxListOptions:[],search:"",visibleCheckboxListOptions:[],init(){this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.$nextTick(()=>{this.checkIfAllCheckboxesAreChecked()}),Livewire.interceptMessage(({message:e,onSuccess:i})=>{i(()=>{this.$nextTick(()=>{e.component.id===s&&(this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked())})})}),this.$watch("search",()=>{this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked()})},checkIfAllCheckboxesAreChecked(){this.areAllCheckboxesChecked=this.visibleCheckboxListOptions.length===this.visibleCheckboxListOptions.filter(e=>e.querySelector("input[type=checkbox]:checked, input[type=checkbox]:disabled")).length},toggleAllCheckboxes(){this.checkIfAllCheckboxesAreChecked();let e=!this.areAllCheckboxesChecked;this.visibleCheckboxListOptions.forEach(i=>{let t=i.querySelector("input[type=checkbox]");t.disabled||t.checked!==e&&(t.checked=e,t.dispatchEvent(new Event("change")))}),this.areAllCheckboxesChecked=e},updateVisibleCheckboxListOptions(){this.visibleCheckboxListOptions=this.checkboxListOptions.filter(e=>["",null,void 0].includes(this.search)||e.querySelector(".fi-fo-checkbox-list-option-label")?.innerText.toLowerCase().includes(this.search.toLowerCase())?!0:e.querySelector(".fi-fo-checkbox-list-option-description")?.innerText.toLowerCase().includes(this.search.toLowerCase()))}}}export{c as default};
|
||||
function c({livewireId:s}){return{areAllCheckboxesChecked:!1,checkboxListOptions:[],search:"",unsubscribeLivewireHook:null,visibleCheckboxListOptions:[],init(){this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.$nextTick(()=>{this.checkIfAllCheckboxesAreChecked()}),this.unsubscribeLivewireHook=Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{e.component.id===s&&(this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked())})})}),this.$watch("search",()=>{this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked()})},checkIfAllCheckboxesAreChecked(){this.areAllCheckboxesChecked=this.visibleCheckboxListOptions.length===this.visibleCheckboxListOptions.filter(e=>e.querySelector("input[type=checkbox]:checked, input[type=checkbox]:disabled")).length},toggleAllCheckboxes(){this.checkIfAllCheckboxesAreChecked();let e=!this.areAllCheckboxesChecked;this.visibleCheckboxListOptions.forEach(t=>{let i=t.querySelector("input[type=checkbox]");i.disabled||i.checked!==e&&(i.checked=e,i.dispatchEvent(new Event("change")))}),this.areAllCheckboxesChecked=e},updateVisibleCheckboxListOptions(){this.visibleCheckboxListOptions=this.checkboxListOptions.filter(e=>["",null,void 0].includes(this.search)||e.querySelector(".fi-fo-checkbox-list-option-label")?.innerText.toLowerCase().includes(this.search.toLowerCase())?!0:e.querySelector(".fi-fo-checkbox-list-option-description")?.innerText.toLowerCase().includes(this.search.toLowerCase()))},destroy(){this.unsubscribeLivewireHook?.()}}}export{c as default};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
function h({state:r}){return{state:r,rows:[],init(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(e,t)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(e)===0&&s(t)===0||this.updateRows()})},addRow(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow(e){this.rows.splice(e,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows(e){let t=Alpine.raw(this.rows);this.rows=[];let s=t.splice(e.oldIndex,1)[0];t.splice(e.newIndex,0,s),this.$nextTick(()=>{this.rows=t,this.updateState()})},updateRows(){let t=Alpine.raw(this.state).map(({key:s,value:i})=>({key:s,value:i}));this.rows.forEach(s=>{(s.key===""||s.key===null)&&t.push({key:"",value:s.value})}),this.rows=t},updateState(){let e=[];this.rows.forEach(t=>{t.key===""||t.key===null||e.push({key:t.key,value:t.value})}),JSON.stringify(this.state)!==JSON.stringify(e)&&(this.state=e)}}}export{h as default};
|
||||
function a({state:r}){return{state:r,rows:[],init(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(e,t)=>{if(!Array.isArray(e))return;let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(e)===0&&s(t)===0||this.updateRows()})},addRow(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow(e){this.rows.splice(e,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows(e){let t=Alpine.raw(this.rows);this.rows=[];let s=t.splice(e.oldIndex,1)[0];t.splice(e.newIndex,0,s),this.$nextTick(()=>{this.rows=t,this.updateState()})},updateRows(){let t=Alpine.raw(this.state).map(({key:s,value:i})=>({key:s,value:i}));this.rows.forEach(s=>{(s.key===""||s.key===null)&&t.push({key:"",value:s.value})}),this.rows=t},updateState(){let e=[];this.rows.forEach(t=>{t.key===""||t.key===null||e.push({key:t.key,value:t.value})}),JSON.stringify(this.state)!==JSON.stringify(e)&&(this.state=e)}}}export{a as default};
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
function I({activeTab:w,isScrollable:f,isTabPersistedInQueryString:g,livewireId:m,tab:T,tabQueryStringKey:c}){return{boundResizeHandler:null,isScrollable:f,resizeDebounceTimer:null,tab:T,withinDropdownIndex:null,withinDropdownMounted:!1,init(){let t=this.getTabs(),e=new URLSearchParams(window.location.search);g&&e.has(c)&&t.includes(e.get(c))&&(this.tab=e.get(c)),this.$watch("tab",()=>this.updateQueryString()),(!this.tab||!t.includes(this.tab))&&(this.tab=t[w-1]),Livewire.interceptMessage(({message:n,onSuccess:o})=>{o(()=>{this.$nextTick(()=>{if(n.component.id!==m)return;let l=this.getTabs();l.includes(this.tab)||(this.tab=l[w-1]??this.tab)})})}),f||(this.boundResizeHandler=this.debouncedUpdateTabsWithinDropdown.bind(this),window.addEventListener("resize",this.boundResizeHandler),this.updateTabsWithinDropdown())},calculateAvailableWidth(t){let e=window.getComputedStyle(t);return Math.floor(t.clientWidth)-Math.ceil(parseFloat(e.paddingLeft))*2},calculateContainerGap(t){let e=window.getComputedStyle(t);return Math.ceil(parseFloat(e.columnGap))},calculateDropdownIconWidth(t){let e=t.querySelector(".fi-icon");return Math.ceil(e.clientWidth)},calculateTabItemGap(t){let e=window.getComputedStyle(t);return Math.ceil(parseFloat(e.columnGap)||8)},calculateTabItemPadding(t){let e=window.getComputedStyle(t);return Math.ceil(parseFloat(e.paddingLeft))+Math.ceil(parseFloat(e.paddingRight))},findOverflowIndex(t,e,n,o,l,h){let u=t.map(i=>Math.ceil(i.clientWidth)),p=t.map(i=>{let d=i.querySelector(".fi-tabs-item-label"),a=i.querySelector(".fi-badge"),s=Math.ceil(d.clientWidth),r=a?Math.ceil(a.clientWidth):0;return{label:s,badge:r,total:s+(r>0?o+r:0)}});for(let i=0;i<t.length;i++){let d=u.slice(0,i+1).reduce((b,y)=>b+y,0),a=i*n,s=p.slice(i+1),r=s.length>0,W=r?Math.max(...s.map(b=>b.total)):0,D=r?l+W+o+h+n:0;if(d+a+D>e)return i}return-1},get isDropdownButtonVisible(){return this.withinDropdownMounted?this.withinDropdownIndex===null?!1:this.getTabs().findIndex(e=>e===this.tab)<this.withinDropdownIndex:!0},getTabs(){return this.$refs.tabsData?JSON.parse(this.$refs.tabsData.value):[]},updateQueryString(){if(!g)return;let t=new URL(window.location.href);t.searchParams.set(c,this.tab),history.replaceState(null,document.title,t.toString())},debouncedUpdateTabsWithinDropdown(){clearTimeout(this.resizeDebounceTimer),this.resizeDebounceTimer=setTimeout(()=>this.updateTabsWithinDropdown(),150)},async updateTabsWithinDropdown(){this.withinDropdownIndex=null,this.withinDropdownMounted=!1,await this.$nextTick();let t=this.$el.querySelector(".fi-tabs"),e=t.querySelector(".fi-tabs-item:last-child"),n=Array.from(t.children).slice(0,-1),o=n.map(a=>a.style.display);n.forEach(a=>a.style.display=""),t.offsetHeight;let l=this.calculateAvailableWidth(t),h=this.calculateContainerGap(t),u=this.calculateDropdownIconWidth(e),p=this.calculateTabItemGap(n[0]),i=this.calculateTabItemPadding(n[0]),d=this.findOverflowIndex(n,l,h,p,i,u);n.forEach((a,s)=>a.style.display=o[s]),d!==-1&&(this.withinDropdownIndex=d),this.withinDropdownMounted=!0},destroy(){this.boundResizeHandler&&window.removeEventListener("resize",this.boundResizeHandler),clearTimeout(this.resizeDebounceTimer)}}}export{I as default};
|
||||
function I({activeTab:w,isScrollable:f,isTabPersistedInQueryString:g,livewireId:m,tab:T,tabQueryStringKey:c}){return{boundResizeHandler:null,isScrollable:f,resizeDebounceTimer:null,tab:T,unsubscribeLivewireHook:null,withinDropdownIndex:null,withinDropdownMounted:!1,init(){let t=this.getTabs(),e=new URLSearchParams(window.location.search);g&&e.has(c)&&t.includes(e.get(c))&&(this.tab=e.get(c)),this.$watch("tab",()=>this.updateQueryString()),(!this.tab||!t.includes(this.tab))&&(this.tab=t[w-1]),this.unsubscribeLivewireHook=Livewire.interceptMessage(({message:n,onSuccess:o})=>{o(()=>{this.$nextTick(()=>{if(n.component.id!==m)return;let l=this.getTabs();l.includes(this.tab)||(this.tab=l[w-1]??this.tab)})})}),f||(this.boundResizeHandler=this.debouncedUpdateTabsWithinDropdown.bind(this),window.addEventListener("resize",this.boundResizeHandler),this.updateTabsWithinDropdown())},calculateAvailableWidth(t){let e=window.getComputedStyle(t);return Math.floor(t.clientWidth)-Math.ceil(parseFloat(e.paddingLeft))*2},calculateContainerGap(t){let e=window.getComputedStyle(t);return Math.ceil(parseFloat(e.columnGap))},calculateDropdownIconWidth(t){let e=t.querySelector(".fi-icon");return Math.ceil(e.clientWidth)},calculateTabItemGap(t){let e=window.getComputedStyle(t);return Math.ceil(parseFloat(e.columnGap)||8)},calculateTabItemPadding(t){let e=window.getComputedStyle(t);return Math.ceil(parseFloat(e.paddingLeft))+Math.ceil(parseFloat(e.paddingRight))},findOverflowIndex(t,e,n,o,l,h){let u=t.map(i=>Math.ceil(i.clientWidth)),b=t.map(i=>{let d=i.querySelector(".fi-tabs-item-label"),s=i.querySelector(".fi-badge"),a=Math.ceil(d.clientWidth),r=s?Math.ceil(s.clientWidth):0;return{label:a,badge:r,total:a+(r>0?o+r:0)}});for(let i=0;i<t.length;i++){let d=u.slice(0,i+1).reduce((p,y)=>p+y,0),s=i*n,a=b.slice(i+1),r=a.length>0,W=r?Math.max(...a.map(p=>p.total)):0,D=r?l+W+o+h+n:0;if(d+s+D>e)return i}return-1},get isDropdownButtonVisible(){return this.withinDropdownMounted?this.withinDropdownIndex===null?!1:this.getTabs().findIndex(e=>e===this.tab)<this.withinDropdownIndex:!0},getTabs(){return this.$refs.tabsData?JSON.parse(this.$refs.tabsData.value):[]},updateQueryString(){if(!g)return;let t=new URL(window.location.href);t.searchParams.set(c,this.tab),history.replaceState(null,document.title,t.toString())},debouncedUpdateTabsWithinDropdown(){clearTimeout(this.resizeDebounceTimer),this.resizeDebounceTimer=setTimeout(()=>this.updateTabsWithinDropdown(),150)},async updateTabsWithinDropdown(){this.withinDropdownIndex=null,this.withinDropdownMounted=!1,await this.$nextTick();let t=this.$el.querySelector(".fi-tabs"),e=t.querySelector(".fi-tabs-item:last-child"),n=Array.from(t.children).slice(0,-1),o=n.map(s=>s.style.display);n.forEach(s=>s.style.display=""),t.offsetHeight;let l=this.calculateAvailableWidth(t),h=this.calculateContainerGap(t),u=this.calculateDropdownIconWidth(e),b=this.calculateTabItemGap(n[0]),i=this.calculateTabItemPadding(n[0]),d=this.findOverflowIndex(n,l,h,b,i,u);n.forEach((s,a)=>s.style.display=o[a]),d!==-1&&(this.withinDropdownIndex=d),this.withinDropdownMounted=!0},destroy(){this.unsubscribeLivewireHook?.(),this.boundResizeHandler&&window.removeEventListener("resize",this.boundResizeHandler),clearTimeout(this.resizeDebounceTimer)}}}export{I as default};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
function a({name:i,recordKey:s,state:n}){return{error:void 0,isLoading:!1,state:n,init(){Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{if(this.isLoading||e.component.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let r=this.getServerState();r===void 0||Alpine.raw(this.state)===r||(this.state=r)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let t=await this.$wire.updateTableColumnState(i,s,this.state);this.error=t?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{a as default};
|
||||
function a({name:r,recordKey:s,state:n}){return{error:void 0,isLoading:!1,state:n,unsubscribeLivewireHook:null,init(){this.unsubscribeLivewireHook=Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{if(this.isLoading||e.component.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let i=this.getServerState();i===void 0||Alpine.raw(this.state)===i||(this.state=i)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let t=await this.$wire.updateTableColumnState(r,s,this.state);this.error=t?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)},destroy(){this.unsubscribeLivewireHook?.()}}}export{a as default};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
function a({name:i,recordKey:s,state:n}){return{error:void 0,isLoading:!1,state:n,init(){Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{if(this.isLoading||e.component.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let r=this.getServerState();r===void 0||this.getNormalizedState()===r||(this.state=r)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||this.getNormalizedState()===e)return;this.isLoading=!0;let t=await this.$wire.updateTableColumnState(i,s,this.state);this.error=t?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.getNormalizedState()),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[null,void 0].includes(this.$refs.serverState.value)?"":this.$refs.serverState.value.replaceAll('\\"','"')},getNormalizedState(){let e=Alpine.raw(this.state);return[null,void 0].includes(e)?"":e}}}export{a as default};
|
||||
function a({name:i,recordKey:s,state:n}){return{error:void 0,isLoading:!1,state:n,unsubscribeLivewireHook:null,init(){this.unsubscribeLivewireHook=Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{if(this.isLoading||e.component.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let r=this.getServerState();r===void 0||this.getNormalizedState()===r||(this.state=r)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||this.getNormalizedState()===e)return;this.isLoading=!0;let t=await this.$wire.updateTableColumnState(i,s,this.state);this.error=t?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.getNormalizedState()),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[null,void 0].includes(this.$refs.serverState.value)?"":this.$refs.serverState.value.replaceAll('\\"','"')},getNormalizedState(){let e=Alpine.raw(this.state);return[null,void 0].includes(e)?"":e},destroy(){this.unsubscribeLivewireHook?.()}}}export{a as default};
|
||||
|
||||
@ -1 +1 @@
|
||||
function a({name:i,recordKey:s,state:n}){return{error:void 0,isLoading:!1,state:n,init(){Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{if(this.isLoading||e.component.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let r=this.getServerState();r===void 0||Alpine.raw(this.state)===r||(this.state=r)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let t=await this.$wire.updateTableColumnState(i,s,this.state);this.error=t?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{a as default};
|
||||
function a({name:r,recordKey:s,state:n}){return{error:void 0,isLoading:!1,state:n,unsubscribeLivewireHook:null,init(){this.unsubscribeLivewireHook=Livewire.interceptMessage(({message:e,onSuccess:t})=>{t(()=>{this.$nextTick(()=>{if(this.isLoading||e.component.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let i=this.getServerState();i===void 0||Alpine.raw(this.state)===i||(this.state=i)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let t=await this.$wire.updateTableColumnState(r,s,this.state);this.error=t?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)},destroy(){this.unsubscribeLivewireHook?.()}}}export{a as default};
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
34
specs/102-filament-5-2-1-upgrade/checklists/requirements.md
Normal file
34
specs/102-filament-5-2-1-upgrade/checklists/requirements.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Specification Quality Checklist: Filament v5.2.1 Upgrade
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-02-20
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- This spec references the upstream framework name/version because the feature itself is a patch-level dependency upgrade. It intentionally avoids runbook-level details (commands, tooling, file paths) and instead specifies outcomes and verification.
|
||||
9
specs/102-filament-5-2-1-upgrade/contracts/README.md
Normal file
9
specs/102-filament-5-2-1-upgrade/contracts/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Contracts: Filament v5.2.1 Upgrade
|
||||
|
||||
This feature introduces **no new API contracts** and **no Microsoft Graph contract changes**.
|
||||
|
||||
- No new endpoints
|
||||
- No changes to `config/graph_contracts.php`
|
||||
- No new external integrations
|
||||
|
||||
Verification focus is framework/runtime compatibility and UI regression risk.
|
||||
10
specs/102-filament-5-2-1-upgrade/data-model.md
Normal file
10
specs/102-filament-5-2-1-upgrade/data-model.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Data Model: Filament v5.2.1 Upgrade
|
||||
|
||||
This feature is a **dependency-only** upgrade.
|
||||
|
||||
- No new entities
|
||||
- No schema changes
|
||||
- No data ownership changes
|
||||
- No state transitions introduced
|
||||
|
||||
Implication: Any behavior change observed after the upgrade is treated as a regression unless explicitly documented as an upstream bugfix that we accept.
|
||||
123
specs/102-filament-5-2-1-upgrade/plan.md
Normal file
123
specs/102-filament-5-2-1-upgrade/plan.md
Normal file
@ -0,0 +1,123 @@
|
||||
# Implementation Plan: Filament v5.2.1 Upgrade
|
||||
|
||||
**Branch**: `feat/102-filament-5-2-1-upgrade` | **Date**: 2026-02-20 | **Spec**: `specs/102-filament-5-2-1-upgrade/spec.md`
|
||||
**Input**: Feature specification from `/specs/102-filament-5-2-1-upgrade/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Upgrade Filament to v5.2.1 (or newer within 5.2.x) as a **dependency-only** change.
|
||||
|
||||
Verification gates (per spec clarifications):
|
||||
|
||||
- Automated: full test suite + frontend build
|
||||
- Manual: staging UI smoke (Admin `/admin/*` + System `/system/*` if present)
|
||||
|
||||
No application feature work, no schema changes, and no new Graph calls are in scope.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: PHP 8.4.x (runtime), Composer constraint `php: ^8.2`
|
||||
**Primary Dependencies**: Laravel 12, Filament 5, Livewire 4
|
||||
**Storage**: PostgreSQL (Sail), plus filesystem storage for logs/cache
|
||||
**Testing**: Pest (via `artisan test`)
|
||||
**Target Platform**: Web application (Sail locally; Dokploy containers in staging/prod)
|
||||
**Project Type**: web
|
||||
**Performance Goals**: N/A (dependency-only patch upgrade)
|
||||
**Constraints**:
|
||||
|
||||
- Dependency-only changes (Composer-only). No application code changes for compatibility.
|
||||
- Test-only changes in `tests/**` are allowed if needed to keep the suite green; no runtime behavior changes.
|
||||
- No new panels/routes/resources. No UX/IA changes intended.
|
||||
- Verification must include `/admin` and `/system` (if System panel exists).
|
||||
- Staging smoke is mandatory.
|
||||
|
||||
**Scale/Scope**: N/A for this change; treat any behavior delta as regression unless it’s a documented upstream bugfix we accept.
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
Result: **PASS**
|
||||
|
||||
- No new Microsoft Graph calls are introduced.
|
||||
- No new write/change flows, jobs, or `OperationRun` patterns are introduced.
|
||||
- No RBAC model changes are introduced; `/admin` and `/system` planes remain separated.
|
||||
- No new or modified Filament Resources/Pages/RelationManagers are introduced (dependency-only upgrade).
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/102-filament-5-2-1-upgrade/
|
||||
├── spec.md
|
||||
├── plan.md
|
||||
├── research.md
|
||||
├── data-model.md
|
||||
├── quickstart.md
|
||||
└── contracts/
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
app/
|
||||
bootstrap/
|
||||
config/
|
||||
resources/
|
||||
routes/
|
||||
tests/
|
||||
```
|
||||
|
||||
**Structure Decision**: Single Laravel web application (Filament panels live under `app/Filament/**`).
|
||||
|
||||
## Phase 0 — Outline & Research
|
||||
|
||||
Goal: confirm upgrade target, identify upstream behavior changes, and map them to verification.
|
||||
|
||||
Deliverable:
|
||||
|
||||
- `research.md` capturing decisions + rationale + alternatives (completed)
|
||||
|
||||
Key research topics:
|
||||
|
||||
- Filament v5.2.0 → v5.2.1 release notes:
|
||||
- tenancy registration behavior when tenancy is not active
|
||||
- bulk “Select all” excluding non-selectable records
|
||||
- corrected guidance around testing assertion replacement
|
||||
|
||||
## Phase 1 — Design & Contracts
|
||||
|
||||
This is a dependency-only upgrade; design outputs focus on explicitly stating “no contract changes” and providing a verification/rollback runbook.
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `data-model.md` (completed; explicitly no schema/entity changes)
|
||||
- `contracts/README.md` (completed; explicitly no API/Graph contract changes)
|
||||
- `quickstart.md` (completed; verification + rollback runbook)
|
||||
|
||||
Agent context update (required):
|
||||
|
||||
- Run `.specify/scripts/bash/update-agent-context.sh copilot` after Phase 1 outputs exist.
|
||||
|
||||
Post-design constitution re-check:
|
||||
|
||||
- Still **PASS** (no new app code introduced).
|
||||
|
||||
## Phase 2 — Implementation Task Planning (for /speckit.tasks)
|
||||
|
||||
Planned task breakdown (to be materialized into `tasks.md` via `/speckit.tasks`):
|
||||
|
||||
1. Composer upgrade tasks
|
||||
- Bump `filament/filament` constraint to `^5.2.1`
|
||||
- Resolve plugin constraints (Composer-only)
|
||||
- Run `php artisan filament:upgrade` if required by Filament upgrade guidance
|
||||
2. Verification tasks (automated)
|
||||
- Run `vendor/bin/sail artisan test --compact`
|
||||
- Run `vendor/bin/sail npm run build`
|
||||
3. Verification tasks (manual)
|
||||
- Staging deploy
|
||||
- Staging UI smoke (Admin + System panels)
|
||||
4. Rollback tasks
|
||||
- Document exact rollback steps in PR description (linked to `quickstart.md`)
|
||||
41
specs/102-filament-5-2-1-upgrade/quickstart.md
Normal file
41
specs/102-filament-5-2-1-upgrade/quickstart.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Quickstart: Filament v5.2.1 Upgrade Verification
|
||||
|
||||
## Preconditions
|
||||
|
||||
- Baseline on `dev` is green (tests passing) before applying the upgrade.
|
||||
- Local environment uses Sail.
|
||||
|
||||
## Local verification (early signal)
|
||||
|
||||
1. Bring up services: `vendor/bin/sail up -d`
|
||||
2. Install dependencies from lock: `vendor/bin/sail composer install`
|
||||
3. Run automated gate:
|
||||
- Full suite: `vendor/bin/sail artisan test --compact`
|
||||
- Frontend build: `vendor/bin/sail npm run build`
|
||||
|
||||
## Staging verification (required gate)
|
||||
|
||||
Run the UI smoke on staging after deploying the upgrade artifact.
|
||||
|
||||
Must-pass smoke (time-box ≤ 10 minutes):
|
||||
|
||||
- Auth: login/logout for Admin panel; verify System panel access if present
|
||||
- Navigation: load key sections in `/admin` and `/system` without errors
|
||||
- Tenancy behavior: verify no unexpected tenant scoping/registration in `/system`; verify tenant context works as expected in `/admin`
|
||||
- Tables:
|
||||
- search + filters
|
||||
- bulk actions
|
||||
- “Select all” (including correctness with any non-selectable rows)
|
||||
- Modals: create/edit, confirmation dialogs
|
||||
|
||||
## Rollback
|
||||
|
||||
Primary rollback mechanism:
|
||||
|
||||
- Revert `composer.lock` (and any Filament constraint change in `composer.json`) to the last known-good state.
|
||||
- Redeploy the prior known-good artifact.
|
||||
|
||||
Success criteria:
|
||||
|
||||
- Tests + build return to green
|
||||
- Staging UI smoke passes again
|
||||
63
specs/102-filament-5-2-1-upgrade/research.md
Normal file
63
specs/102-filament-5-2-1-upgrade/research.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Research: Filament v5.2.1 Upgrade
|
||||
|
||||
**Feature**: 102-filament-5-2-1-upgrade
|
||||
**Date**: 2026-02-20
|
||||
|
||||
## Upstream Notes (v5.2.0 → v5.2.1)
|
||||
|
||||
Source: Filament GitHub releases (`v5.2.0...v5.2.1`). Highlights relevant to this repo’s verification gates:
|
||||
|
||||
- Tenancy: **"Don't register tenancy on models when it is not active"**
|
||||
- Risk: Multi-panel apps may previously have had unintended tenant scoping or auto-association in a non-tenant panel.
|
||||
- Verification mapping: staging smoke must include both `/admin` and `/system` panels and check for unexpected tenant filtering or missing tenant association.
|
||||
|
||||
- Tables: **"bulk action receives non-selectable records when using \"Select all\""**
|
||||
- Risk: bulk actions can behave incorrectly when tables use non-selectable rows.
|
||||
- Verification mapping: smoke includes select-all + non-selectable-row scenarios.
|
||||
|
||||
- Testing: **"Fix assertHasTableActionErrors() deprecated replacement"**
|
||||
- Risk: tests relying on deprecated assertion guidance may need to follow the corrected replacement pattern.
|
||||
- Verification mapping: full suite must be green; treat any new deprecation warnings or assertion failures as regressions to address.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision: Upgrade Filament constraint to `^5.2.1`
|
||||
|
||||
- Rationale:
|
||||
- Patch-level stability/bugfix upgrade with known fixes relevant to this app (tenancy registration behavior; table bulk selection; testing assertions).
|
||||
- Keeps us current within Filament v5 and reduces upgrade debt.
|
||||
- Alternatives considered:
|
||||
- Stay on `^5.0` until a larger upgrade window is available (rejected: leaves known upstream fixes unconsumed).
|
||||
|
||||
### Decision: Allow plugin/extension dependency bumps (Composer-only) in the same PR
|
||||
|
||||
- Rationale:
|
||||
- Patch upgrades can be blocked by transitive constraints; allowing composer-only plugin bumps preserves the “dependency-only” scope.
|
||||
- Avoids splitting into multiple PRs that each touch `composer.lock` while still preventing application code changes.
|
||||
- Alternatives considered:
|
||||
- Strict Filament-only bump; abort on plugin incompatibility (rejected: increases likelihood of blocking the upgrade).
|
||||
- Allow minimal application code compatibility fixes (rejected: conflicts with “no feature changes” scope).
|
||||
|
||||
### Decision: Verification gates
|
||||
|
||||
- Automated gate (required): **Full automated regression suite + frontend build**
|
||||
- Rationale: ensures both PHP/Livewire/Filament and frontend asset pipeline remain stable.
|
||||
- Alternatives considered: tests-only (rejected: build failures often surface asset regressions).
|
||||
|
||||
- Manual gate (required): **Staging UI smoke** for Admin (`/admin`) and System (`/system`, if present)
|
||||
- Rationale: catches runtime regressions that can slip past tests (modals, table selection, tenancy-related UI behavior).
|
||||
- Alternatives considered: local-only smoke (rejected: staging is closer to production and aligns with rollback criteria).
|
||||
|
||||
### Decision: Rollback strategy
|
||||
|
||||
- Rationale:
|
||||
- Fastest safe rollback is to revert `composer.lock` (and the Filament constraint if changed) and redeploy the last known-good artifact.
|
||||
- Alternatives considered:
|
||||
- “Forward fix” under pressure (rejected: increases outage window and risk).
|
||||
|
||||
## Implementation Outcome Notes (2026-02-20)
|
||||
|
||||
- Filament was upgraded to `v5.2.1` successfully.
|
||||
- No additional Filament plugin/extension constraints were required in `composer.json` (no plugin bumps).
|
||||
- Composer resolution included transitive framework updates (not plugin-specific), including Laravel `v12.52.0` and Livewire `v4.1.4`.
|
||||
- During verification, a tenant-scope regression surfaced in backup-set listing tests after the upgrade; mitigation was applied by explicitly scoping `BackupSetResource` queries to the active tenant.
|
||||
126
specs/102-filament-5-2-1-upgrade/spec.md
Normal file
126
specs/102-filament-5-2-1-upgrade/spec.md
Normal file
@ -0,0 +1,126 @@
|
||||
# Feature Specification: Filament v5.2.1 Upgrade
|
||||
|
||||
**Feature Branch**: `feat/102-filament-5-2-1-upgrade`
|
||||
**Created**: 2026-02-20
|
||||
**Status**: Draft
|
||||
**Input**: User description: "Upgrade the Filament admin panel framework to v5.2.1 (patch-level), with enterprise-grade verification and no feature changes."
|
||||
|
||||
## Spec Scope Fields *(mandatory)*
|
||||
|
||||
- **Scope**: workspace
|
||||
- **Primary Routes**: Filament panels: Admin (`/admin/*`) and System (`/system/*`, if present)
|
||||
- **Data Ownership**: No data model or schema changes are intended (dependency-only change)
|
||||
- **RBAC**: No changes intended; existing membership + capability enforcement remains the source of truth
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-02-20
|
||||
|
||||
- Q: Which Filament panel(s) are in the verification gate (smoke + no runtime errors)? → A: Admin + System panels (`/admin/*` and `/system/*`, if the System panel exists).
|
||||
- Q: How should plugin incompatibilities be handled during the upgrade? → A: Allow plugin version bumps in the same PR, but no application code changes (composer-only).
|
||||
- Q: Where must the UI smoke be executed for the verification gate? → A: Staging UI smoke is required (local smoke is optional/early).
|
||||
- Q: What must the automated verification gate include beyond the test suite? → A: Full automated regression suite + frontend build.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The current mainline build is green (automated regression suite passing) before the upgrade is applied.
|
||||
- The application already meets the upstream framework requirements for Filament v5 (this change is patch-level, not a major migration).
|
||||
- No UI/IA changes are intended; any observed behavior change during verification is treated as a regression.
|
||||
- If the automated regression suite fails due to upstream test assertion/deprecation changes, test-only updates under `tests/**` are allowed to restore a green baseline; runtime application code remains unchanged.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- All currently installed Filament extensions/plugins used by the application are compatible with v5.2.1.
|
||||
- If compatibility requires it, Filament plugin/extension packages MAY be upgraded in the same PR as part of this dependency-only change, provided no application code changes are required.
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Ship the patch upgrade safely (Priority: P1)
|
||||
|
||||
As a platform engineer, I want the admin UI framework upgraded to v5.2.1 so that we pick up upstream stability and bug fixes without changing product behavior.
|
||||
|
||||
**Why this priority**: This unblocks maintenance, reduces risk of known upstream issues, and keeps upgrade debt low.
|
||||
|
||||
**Independent Test**: The change is successful if the automated regression suite is green and the application boots into the admin panel(s) without runtime errors.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the current mainline build is passing automated verification, **When** the upgrade is applied, **Then** the automated regression suite remains fully passing.
|
||||
2. **Given** the upgrade is applied, **When** an authenticated admin user opens the Admin panel landing page and (if present) the System panel landing page, **Then** each page renders without errors and navigation is usable.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Validate core admin workflows (Priority: P2)
|
||||
|
||||
As an operator/QA, I want a short, repeatable UI smoke to confirm that the most critical admin workflows still work after the upgrade.
|
||||
|
||||
**Why this priority**: Patch upgrades can regress tables/actions/modals; a fast smoke test catches high-impact breakage early.
|
||||
|
||||
**Independent Test**: A human can complete the smoke checklist in 5–10 minutes without encountering a blocking error.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an authenticated user in the Admin panel and (if present) the System panel, **When** they navigate between key sections, **Then** navigation loads each page without errors.
|
||||
2. **Given** a list/table view, **When** the user selects multiple rows and uses bulk actions (including “select all”), **Then** selections and bulk actions behave correctly and do not affect non-selectable rows.
|
||||
3. **Given** create/edit flows, **When** the user opens and confirms modals (including destructive confirmations), **Then** modals display and complete without runtime errors.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Roll back confidently if needed (Priority: P3)
|
||||
|
||||
As a platform engineer, I want a clear rollback so that any unexpected regression can be reversed quickly.
|
||||
|
||||
**Why this priority**: Dependency upgrades can have transitive compatibility issues; rollback is the safety net.
|
||||
|
||||
**Independent Test**: Rollback instructions are complete enough that another engineer can restore the previous known-good dependency set.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a regression is found during verification, **When** rollback steps are executed, **Then** the previous dependency set is restored and verification returns to green.
|
||||
|
||||
### Edge Cases
|
||||
- Extension/plugin compatibility breaks at runtime (boot failures, missing classes, broken UI components).
|
||||
- Composer-level plugin constraints block resolution to Filament v5.2.1 without bumping one or more plugin versions.
|
||||
- Tenancy-related behavior changes (e.g., tenancy not being active where expected, or tenancy being registered when it should not be).
|
||||
- Table selection edge cases (select-all, mixed selectable/non-selectable rows) behave inconsistently.
|
||||
- Automated regression suite is green but UI smoke finds a blocking runtime error (e.g., modal/action rendering).
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
**Constitution alignment (required):** This change MUST NOT introduce any new Microsoft Graph calls, new write/change behaviors, or new long-running/queued/scheduled work.
|
||||
|
||||
**Constitution alignment (RBAC-UX):** No authorization model changes are intended. Any discovered RBAC behavior changes during verification MUST be treated as a regression and resolved before shipping.
|
||||
|
||||
**Constitution alignment (Filament Action Surfaces):** No Filament Resource / RelationManager / Page behavior is intended to change. If any action surface behavior changes as a side effect of the upgrade, it MUST be documented and explicitly verified as non-regressive.
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: The system MUST upgrade Filament to version 5.2.1 or newer within the 5.2.x patch line.
|
||||
- **FR-002**: The system MUST preserve existing admin panel behavior (no intentional user-facing feature changes).
|
||||
- **FR-003**: The system MUST remain compatible with all currently installed Filament extensions/plugins used by the application.
|
||||
- **FR-004**: The system MUST pass the full automated regression suite after the upgrade.
|
||||
- **FR-005**: The system MUST pass the defined UI smoke checklist without blocking errors.
|
||||
- **FR-006**: The system MUST provide a documented rollback procedure that restores the previous dependency set.
|
||||
- **FR-007**: The upgrade MUST include a review of upstream release notes from 5.2.0 → 5.2.1, with any relevant behavioral changes mapped to verification steps.
|
||||
- **FR-008**: The verification gate MUST include both the Admin panel and (if present) the System panel.
|
||||
- **FR-009**: If plugin/extension dependency upgrades are required to achieve Filament v5.2.1 compatibility, they MUST be limited to dependency changes (no application code changes).
|
||||
- **FR-010**: The UI smoke verification MUST be executed on staging as part of the go/no-go gate (local smoke is optional/early signal).
|
||||
- **FR-011**: The automated verification gate MUST include the full automated regression suite and a successful frontend build.
|
||||
|
||||
## UI Action Matrix *(mandatory when Filament is changed)*
|
||||
|
||||
This is a dependency-only upgrade. No specific action surfaces are intentionally added or modified.
|
||||
|
||||
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| Dependency upgrade only | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | Verify existing surfaces via smoke checklist and regression suite. |
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: 100% of the automated regression suite passes on the upgrade branch.
|
||||
- **SC-002**: The UI smoke checklist can be completed on staging in ≤ 10 minutes with 0 blocking errors.
|
||||
- **SC-003**: No new “critical” regressions are introduced in admin workflows (0 release-blocking issues found during verification).
|
||||
- **SC-004**: Rollback to the previous dependency set can be executed in ≤ 15 minutes on staging (measured from decision-to-rollback to green verification).
|
||||
- **SC-005**: The frontend build completes successfully on the upgrade branch.
|
||||
148
specs/102-filament-5-2-1-upgrade/tasks.md
Normal file
148
specs/102-filament-5-2-1-upgrade/tasks.md
Normal file
@ -0,0 +1,148 @@
|
||||
---
|
||||
|
||||
description: "Task breakdown for Filament v5.2.1 upgrade (dependency-only)"
|
||||
---
|
||||
|
||||
# Tasks: Filament v5.2.1 Upgrade
|
||||
|
||||
**Input**: Design documents from `/specs/102-filament-5-2-1-upgrade/`
|
||||
**Prerequisites**: plan.md (required), spec.md (required), research.md, data-model.md, contracts/, quickstart.md
|
||||
|
||||
**Organization**: Tasks are grouped by user story (US1–US3) so each story can be independently verified.
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Ensure the feature workspace is ready and the plan/spec artifacts are the source of truth.
|
||||
|
||||
- [X] T001 Verify spec artifacts exist and are consistent in `specs/102-filament-5-2-1-upgrade/{spec.md,plan.md,research.md,quickstart.md,data-model.md,contracts/README.md}`
|
||||
- [X] T002 Capture current baseline dependency state by recording the pre-upgrade contents of `composer.json` and `composer.lock` (for rollback comparison)
|
||||
- [X] T003 [P] Confirm local tooling entrypoints are present for verification (`vendor/bin/sail`, `package.json`, `vite.config.js`)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Establish the verification baseline and guardrails before touching dependencies.
|
||||
|
||||
**⚠️ CRITICAL**: No Filament upgrade work begins until this phase completes.
|
||||
|
||||
- [X] T004 Ensure local environment is runnable via Sail using `docker-compose.yml` (containers up, app boots)
|
||||
- [X] T005 Run Composer sanity checks before upgrading (`vendor/bin/sail composer validate`) against `composer.json`
|
||||
- [X] T006 Run Node dependency install for a clean build baseline using npm (`vendor/bin/sail npm ci`) with `package-lock.json`
|
||||
- [X] T007 Establish a green baseline on current mainline by running `vendor/bin/sail artisan test --compact` for `tests/` (record failures; do not proceed if baseline is red)
|
||||
- [X] T008 Establish a green baseline frontend build by running `vendor/bin/sail npm run build` from `package.json` (do not proceed if baseline build fails)
|
||||
|
||||
**Checkpoint**: Baseline is green; dependency upgrade work can begin.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 — Ship the patch upgrade safely (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Upgrade Filament to v5.2.1 (5.2.x) as a dependency-only change and keep automated verification green.
|
||||
|
||||
**Independent Test**: `vendor/bin/sail artisan test --compact` passes and `vendor/bin/sail npm run build` succeeds; app boots without runtime errors in Admin/System panels.
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [X] T009 [US1] Update Filament constraint to `^5.2.1` in `composer.json`
|
||||
- [X] T010 [US1] Update dependencies and lockfile using Sail Composer (targeted) after `composer.json` change: `vendor/bin/sail composer update filament/filament --with-all-dependencies --no-interaction` (changes limited to `composer.json` and `composer.lock`)
|
||||
- [X] T011 [US1] If Composer resolution requires it, bump Filament plugins/extensions in `composer.json` (Composer-only) and re-resolve `composer.lock`
|
||||
- [ ] T012 [P] [US1] Verify the diff is dependency-only: no changes under `app/`, `config/`, `routes/`, `resources/` (excluding lockfile-related artifacts)
|
||||
- [X] T013 [P] [US1] Run `vendor/bin/sail composer install` to confirm `composer.lock` is reproducible
|
||||
- [X] T014 [P] [US1] Run automated regression suite: `vendor/bin/sail artisan test --compact` for `tests/`
|
||||
- [X] T015 [P] [US1] Run frontend build: `vendor/bin/sail npm run build` using `package.json`
|
||||
- [X] T016 [US1] If tests fail due to assertion/deprecation changes, update only the failing tests in `tests/**` (no runtime code changes) until the suite is green
|
||||
- [X] T017 [US1] Confirm Filament upgrade automation did not introduce unintended code changes; if `composer.json` triggers `php artisan filament:upgrade`, ensure no modifications occurred outside dependency scope
|
||||
|
||||
**Checkpoint**: Filament v5.2.1 upgrade is complete and automated gate is green.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 — Validate core admin workflows (Priority: P2)
|
||||
|
||||
**Goal**: Run a short staging UI smoke that targets known risk areas from the upstream patch notes.
|
||||
|
||||
**Independent Test**: Complete `specs/102-filament-5-2-1-upgrade/quickstart.md` staging smoke in ≤10 minutes with 0 blocking errors.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [ ] T018 [US2] Deploy the upgraded artifact to staging and confirm the smoke checklist source is `specs/102-filament-5-2-1-upgrade/quickstart.md`
|
||||
- [ ] T019 [US2] Smoke: login/logout and panel access for Admin (`/admin/*`) and System (`/system/*`, if present) per `specs/102-filament-5-2-1-upgrade/spec.md`
|
||||
- [ ] T020 [US2] Smoke: navigate key Monitoring areas starting from `app/Filament/Clusters/Monitoring/` and confirm pages render without runtime errors
|
||||
- [ ] T021 [US2] Smoke: validate tenancy behavior aligns with upstream fix (no tenancy registered when not active) using the checklist in `specs/102-filament-5-2-1-upgrade/quickstart.md` and upstream mapping in `specs/102-filament-5-2-1-upgrade/research.md`
|
||||
- [ ] T022 [US2] Smoke: validate table bulk actions + “Select all” on concrete list pages that implement it: `app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php` and `app/Filament/Resources/RestoreRunResource/Pages/ListRestoreRuns.php` (also confirm non-selectable rows are excluded if applicable)
|
||||
- [ ] T023 [US2] Smoke: validate create/edit modals and confirmation dialogs still render and complete successfully (no JS/runtime errors) per `specs/102-filament-5-2-1-upgrade/quickstart.md`
|
||||
|
||||
**Checkpoint**: Staging smoke passes and upgrade is go/no-go ready.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 — Roll back confidently if needed (Priority: P3)
|
||||
|
||||
**Goal**: Ensure rollback is concrete, accurate, and fast.
|
||||
|
||||
**Independent Test**: Another engineer can follow the rollback steps and restore the previous dependency set.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [ ] T024 [US3] Validate rollback instructions match the actual changed files (`composer.json`, `composer.lock`) and update `specs/102-filament-5-2-1-upgrade/quickstart.md` if needed
|
||||
- [ ] T025 [US3] If any plugin versions were bumped, record the exact rollback expectation (revert `composer.lock` and constraint changes) in `specs/102-filament-5-2-1-upgrade/quickstart.md`
|
||||
- [ ] T026 [US3] Perform a time-boxed rollback rehearsal on staging (revert dependency set, redeploy prior artifact) and confirm the automated gate + smoke return to green per `specs/102-filament-5-2-1-upgrade/spec.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Final hygiene checks to ensure the spec/plan/tasks stay aligned with the actual upgrade outcome.
|
||||
|
||||
- [X] T027 [P] Re-run SpecKit prerequisites validation after tasks exist using `.specify/scripts/bash/check-prerequisites.sh` and confirm `specs/102-filament-5-2-1-upgrade/tasks.md` is detected
|
||||
- [X] T028 Ensure `specs/102-filament-5-2-1-upgrade/research.md` accurately reflects any plugin bumps that were required by Composer resolution
|
||||
- [X] T029 [P] Run code formatting gate via Pint using `vendor/bin/sail bin pint --dirty --format agent` (expect no runtime-code changes for this dependency-only upgrade; investigate any unexpected diffs)
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies
|
||||
- **Foundational (Phase 2)**: Depends on Setup; BLOCKS all user story work
|
||||
- **US1 (Phase 3)**: Depends on Foundational; MVP scope
|
||||
- **US2 (Phase 4)**: Depends on US1 (needs upgraded artifact)
|
||||
- **US3 (Phase 5)**: Depends on US1; best executed after US2 confirms whether rollback is needed
|
||||
- **Polish (Phase 6)**: After US1–US3 outcomes are known
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1 (P1)** → enables **US2 (P2)** and **US3 (P3)**
|
||||
- **US2 (P2)** should run before deciding whether to execute **US3** rollback in practice
|
||||
|
||||
## Parallel Execution Examples
|
||||
|
||||
### US1 (After T010)
|
||||
|
||||
- Parallel candidates:
|
||||
- T012 (verify diff scope) and T013 (reproducible install) can run in parallel once `composer.lock` is updated.
|
||||
|
||||
### US2
|
||||
|
||||
- Parallel candidates:
|
||||
- T020 (navigation) and T022 (bulk select-all) can be split across two reviewers during the same staging session.
|
||||
|
||||
### US3
|
||||
|
||||
- Parallel candidates:
|
||||
- T024 (doc accuracy) can be done while staging verification (US2) is running.
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (US1 only)
|
||||
|
||||
1. Phase 1 → Phase 2 (baseline green)
|
||||
2. Phase 3 (US1): bump Filament + lockfile, then run tests + build
|
||||
3. Stop and validate: only proceed once automated gate is green
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
- After US1, run US2 staging smoke.
|
||||
- If smoke fails, either fix via dependency resolution (preferred) or roll back using US3.
|
||||
@ -3,6 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Filament\Resources\AlertDeliveryResource;
|
||||
use App\Filament\Resources\AlertDeliveryResource\Pages\ListAlertDeliveries;
|
||||
use App\Models\AlertDelivery;
|
||||
use App\Models\AlertDestination;
|
||||
use App\Models\AlertRule;
|
||||
@ -11,6 +12,7 @@
|
||||
use App\Models\Workspace;
|
||||
use App\Models\WorkspaceMembership;
|
||||
use App\Services\Auth\WorkspaceCapabilityResolver;
|
||||
use Livewire\Livewire;
|
||||
|
||||
it('lists only deliveries for entitled tenants', function (): void {
|
||||
[$user, $tenantA] = createUserWithTenant(role: 'readonly');
|
||||
@ -29,7 +31,7 @@
|
||||
'workspace_id' => $workspaceId,
|
||||
]);
|
||||
|
||||
AlertDelivery::factory()->create([
|
||||
$tenantADelivery = AlertDelivery::factory()->create([
|
||||
'workspace_id' => $workspaceId,
|
||||
'tenant_id' => (int) $tenantA->getKey(),
|
||||
'alert_rule_id' => (int) $rule->getKey(),
|
||||
@ -37,7 +39,7 @@
|
||||
'event_type' => 'high_drift',
|
||||
]);
|
||||
|
||||
AlertDelivery::factory()->create([
|
||||
$tenantBDelivery = AlertDelivery::factory()->create([
|
||||
'workspace_id' => $workspaceId,
|
||||
'tenant_id' => (int) $tenantB->getKey(),
|
||||
'alert_rule_id' => (int) $rule->getKey(),
|
||||
@ -45,11 +47,11 @@
|
||||
'event_type' => 'compare_failed',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(AlertDeliveryResource::getUrl(panel: 'admin'))
|
||||
->assertOk()
|
||||
->assertSee('high_drift')
|
||||
->assertDontSee('compare_failed');
|
||||
$this->actingAs($user);
|
||||
|
||||
Livewire::test(ListAlertDeliveries::class)
|
||||
->assertCanSeeTableRecords([$tenantADelivery])
|
||||
->assertCanNotSeeTableRecords([$tenantBDelivery]);
|
||||
});
|
||||
|
||||
it('returns 404 when a member from another workspace tries to view a delivery', function (): void {
|
||||
|
||||
@ -25,10 +25,16 @@
|
||||
|
||||
bindFailHardGraphClient();
|
||||
|
||||
$this->actingAs($user)
|
||||
$response = $this->actingAs($user)
|
||||
->get(BackupSetResource::getUrl('index', tenant: $tenant))
|
||||
->assertOk()
|
||||
->assertSee('Created by')
|
||||
->assertSee($visibleSet->name)
|
||||
->assertDontSee($hiddenSet->name);
|
||||
->assertSee('Created by');
|
||||
|
||||
$content = (string) $response->getContent();
|
||||
preg_match('/<tbody[^>]*>(.*?)<\\/tbody>/is', $content, $tbodyMatch);
|
||||
$tableCellText = html_entity_decode(strip_tags((string) ($tbodyMatch[1] ?? '')));
|
||||
|
||||
expect($tableCellText)
|
||||
->toContain($visibleSet->name)
|
||||
->not->toContain($hiddenSet->name);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user