22
33namespace App \Filament \Pages ;
44
5+ use App \Helpers \KanbanScrumHelper ;
56use App \Models \Project ;
6- use App \Models \Ticket ;
7- use App \Models \TicketPriority ;
8- use App \Models \TicketStatus ;
9- use App \Models \TicketType ;
10- use App \Models \User ;
117use Filament \Facades \Filament ;
12- use Filament \Forms \Components \Grid ;
13- use Filament \Forms \Components \Placeholder ;
14- use Filament \Forms \Components \Select ;
15- use Filament \Forms \Components \Toggle ;
168use Filament \Forms \Concerns \InteractsWithForms ;
179use Filament \Forms \Contracts \HasForms ;
1810use Filament \Pages \Actions \Action ;
1911use Filament \Pages \Page ;
2012use Illuminate \Contracts \Support \Htmlable ;
21- use Illuminate \Support \Collection ;
22- use Illuminate \Support \HtmlString ;
2313
2414class Kanban extends Page implements HasForms
2515{
26- use InteractsWithForms;
16+ use InteractsWithForms, KanbanScrumHelper ;
2717
2818 protected static ?string $ navigationIcon = 'heroicon-o-view-boards ' ;
2919
@@ -33,17 +23,6 @@ class Kanban extends Page implements HasForms
3323
3424 protected static bool $ shouldRegisterNavigation = false ;
3525
36- public bool $ sortable = true ;
37-
38- public Project |null $ project = null ;
39-
40- public $ users = [];
41- public $ types = [];
42- public $ priorities = [];
43- public $ includeNotAffectedTickets = false ;
44-
45- public bool $ ticket = false ;
46-
4726 protected $ listeners = [
4827 'recordUpdated ' ,
4928 'closeTicketDialog '
@@ -64,53 +43,6 @@ public function mount(Project $project)
6443 $ this ->form ->fill ();
6544 }
6645
67- protected function getFormSchema (): array
68- {
69- return [
70- Grid::make ([
71- 'default ' => 2 ,
72- 'md ' => 6
73- ])
74- ->schema ([
75- Select::make ('users ' )
76- ->label (__ ('Owners / Responsibles ' ))
77- ->multiple ()
78- ->options (User::all ()->pluck ('name ' , 'id ' )),
79-
80- Select::make ('types ' )
81- ->label (__ ('Ticket types ' ))
82- ->multiple ()
83- ->options (TicketType::all ()->pluck ('name ' , 'id ' )),
84-
85- Select::make ('priorities ' )
86- ->label (__ ('Ticket priorities ' ))
87- ->multiple ()
88- ->options (TicketPriority::all ()->pluck ('name ' , 'id ' )),
89-
90- Toggle::make ('includeNotAffectedTickets ' )
91- ->label (__ ('Include not affected tickets ' ))
92- ->columnSpan (2 ),
93-
94- Placeholder::make ('search ' )
95- ->label (new HtmlString (' ' ))
96- ->content (new HtmlString ('
97- <button type="button"
98- wire:click="filter" wire:loading.attr="disabled"
99- class="bg-primary-500 px-3 py-2 text-white rounded hover:bg-primary-600
100- disabled:bg-primary-300">
101- ' . __ ('Filter ' ) . '
102- </button>
103- <button type="button"
104- wire:click="resetFilters" wire:loading.attr="disabled"
105- class="ml-2 bg-gray-800 px-3 py-2 text-white rounded hover:bg-gray-900
106- disabled:bg-gray-300">
107- ' . __ ('Reset filters ' ) . '
108- </button>
109- ' )),
110- ]),
111- ];
112- }
113-
11446 protected function getActions (): array
11547 {
11648 return [
@@ -127,140 +59,12 @@ protected function getActions(): array
12759
12860 protected function getHeading (): string |Htmlable
12961 {
130- $ heading = '<div class="w-full flex flex-col gap-1"> ' ;
131- $ heading .= '<a href=" ' . route ('filament.pages.board ' ) . '"
132- class="text-primary-500 text-xs font-medium hover:underline"> ' ;
133- $ heading .= __ ('Back to board ' );
134- $ heading .= '</a> ' ;
135- $ heading .= '<div class="flex flex-col gap-1"> ' ;
136- $ heading .= '<span> ' . __ ('Kanban ' );
137- if ($ this ->project ) {
138- $ heading .= ' - ' . $ this ->project ->name . '</span> ' ;
139- } else {
140- $ heading .= '</span><span class="text-xs text-gray-400"> '
141- . __ ('Only default statuses are listed when no projects selected ' )
142- . '</span> ' ;
143- }
144- $ heading .= '</div> ' ;
145- $ heading .= '</div> ' ;
146- return new HtmlString ($ heading );
147- }
148-
149- public function getStatuses (): Collection
150- {
151- $ query = TicketStatus::query ();
152- if ($ this ->project && $ this ->project ->status_type === 'custom ' ) {
153- $ query ->where ('project_id ' , $ this ->project ->id );
154- } else {
155- $ query ->whereNull ('project_id ' );
156- }
157- return $ query ->orderBy ('order ' )
158- ->get ()
159- ->map (function ($ item ) {
160- $ query = Ticket::query ();
161- if ($ this ->project ) {
162- $ query ->where ('project_id ' , $ this ->project ->id );
163- }
164- $ query ->where ('status_id ' , $ item ->id );
165- return [
166- 'id ' => $ item ->id ,
167- 'title ' => $ item ->name ,
168- 'color ' => $ item ->color ,
169- 'size ' => $ query ->count (),
170- 'add_ticket ' => $ item ->is_default && auth ()->user ()->can ('Create ticket ' )
171- ];
172- });
173- }
174-
175- public function getRecords (): Collection
176- {
177- $ query = Ticket::query ();
178- $ query ->with (['project ' , 'owner ' , 'responsible ' , 'status ' , 'type ' , 'priority ' , 'epic ' ]);
179- if ($ this ->project ) {
180- $ query ->where ('project_id ' , $ this ->project ->id );
181- } else {
182- $ query ->whereHas ('project ' , fn ($ query ) => $ query ->where ('status_type ' , 'default ' ));
183- }
184- if (sizeof ($ this ->users )) {
185- $ query ->where (function ($ query ) {
186- return $ query ->whereIn ('owner_id ' , $ this ->users )
187- ->orWhereIn ('responsible_id ' , $ this ->users );
188- });
189- }
190- if (sizeof ($ this ->types )) {
191- $ query ->whereIn ('type_id ' , $ this ->types );
192- }
193- if (sizeof ($ this ->priorities )) {
194- $ query ->whereIn ('priority_id ' , $ this ->priorities );
195- }
196- if ($ this ->includeNotAffectedTickets ) {
197- $ query ->orWhereNull ('responsible_id ' );
198- }
199- $ query ->where (function ($ query ) {
200- return $ query ->where ('owner_id ' , auth ()->user ()->id )
201- ->orWhere ('responsible_id ' , auth ()->user ()->id )
202- ->orWhereHas ('project ' , function ($ query ) {
203- return $ query ->where ('owner_id ' , auth ()->user ()->id )
204- ->orWhereHas ('users ' , function ($ query ) {
205- return $ query ->where ('users.id ' , auth ()->user ()->id );
206- });
207- });
208- });
209- return $ query ->get ()
210- ->map (fn (Ticket $ item ) => [
211- 'id ' => $ item ->id ,
212- 'code ' => $ item ->code ,
213- 'title ' => $ item ->name ,
214- 'owner ' => $ item ->owner ,
215- 'type ' => $ item ->type ,
216- 'responsible ' => $ item ->responsible ,
217- 'project ' => $ item ->project ,
218- 'status ' => $ item ->status ->id ,
219- 'priority ' => $ item ->priority ,
220- 'epic ' => $ item ->epic ,
221- 'relations ' => $ item ->relations ,
222- 'totalLoggedHours ' => $ item ->totalLoggedSeconds ? $ item ->totalLoggedHours : null
223- ]);
224- }
225-
226- public function recordUpdated (int $ record , int $ newIndex , int $ newStatus ): void
227- {
228- $ ticket = Ticket::find ($ record );
229- if ($ ticket ) {
230- $ ticket ->order = $ newIndex ;
231- $ ticket ->status_id = $ newStatus ;
232- $ ticket ->save ();
233- Filament::notify ('success ' , __ ('Ticket updated ' ));
234- }
235- }
236-
237- public function isMultiProject (): bool
238- {
239- return $ this ->project === null ;
62+ return $ this ->kanbanHeading ();
24063 }
24164
242- public function filter (): void
243- {
244- $ this ->getRecords ();
245- }
246-
247- public function resetFilters (): void
248- {
249- $ this ->form ->fill ();
250- $ this ->filter ();
251- }
252-
253- public function createTicket (): void
254- {
255- $ this ->ticket = true ;
256- }
257-
258- public function closeTicketDialog (bool $ refresh ): void
65+ protected function getFormSchema (): array
25966 {
260- $ this ->ticket = false ;
261- if ($ refresh ) {
262- $ this ->filter ();
263- }
67+ return $ this ->formSchema ();
26468 }
26569
26670}
0 commit comments