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
30- protected static ?string $ slug = 'kanban ' ;
20+ protected static ?string $ slug = 'kanban/{project} ' ;
3121
3222 protected static string $ view = 'filament.pages.kanban ' ;
3323
34- protected static ?int $ navigationSort = 3 ;
35-
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 ;
24+ protected static bool $ shouldRegisterNavigation = false ;
4625
4726 protected $ listeners = [
4827 'recordUpdated ' ,
4928 'closeTicketDialog '
5029 ];
5130
52- public function mount ()
31+ public function mount (Project $ project )
5332 {
54- if ( request ()-> has ( ' project ' )) {
55- $ this ->project = Project:: find ( request ()-> get ( ' project ' ));
56- if (
57- $ this -> project -> owner_id != auth ()-> user ()-> id
58- &&
59- ! $ this -> project -> users -> where ( ' id ' , auth ()-> user ()-> id )-> count ()
60- ) {
61- abort ( 403 );
62- }
33+ $ this -> project = $ project ;
34+ if ( $ this ->project -> type === ' scrum ' ) {
35+ $ this -> redirect ( route ( ' filament.pages.scrum/{project} ' , [ ' project ' => $ project ]));
36+ } elseif (
37+ $ this -> project -> owner_id != auth ()-> user ()-> id
38+ &&
39+ ! $ this -> project -> users -> where ( ' id ' , auth ()-> user ()-> id )-> count ()
40+ ) {
41+ abort ( 403 );
6342 }
6443 $ this ->form ->fill ();
6544 }
6645
67- protected static function getNavigationLabel (): string
68- {
69- return __ ('Kanban ' );
70- }
71-
72- protected function getFormSchema (): array
73- {
74- return [
75- Grid::make ([
76- 'default ' => 2 ,
77- 'md ' => 6
78- ])
79- ->schema ([
80- Select::make ('users ' )
81- ->label (__ ('Owners / Responsibles ' ))
82- ->multiple ()
83- ->options (User::all ()->pluck ('name ' , 'id ' )),
84-
85- Select::make ('types ' )
86- ->label (__ ('Ticket types ' ))
87- ->multiple ()
88- ->options (TicketType::all ()->pluck ('name ' , 'id ' )),
89-
90- Select::make ('priorities ' )
91- ->label (__ ('Ticket priorities ' ))
92- ->multiple ()
93- ->options (TicketPriority::all ()->pluck ('name ' , 'id ' )),
94-
95- Toggle::make ('includeNotAffectedTickets ' )
96- ->label (__ ('Include not affected tickets ' ))
97- ->columnSpan (2 ),
98-
99- Placeholder::make ('search ' )
100- ->label (new HtmlString (' ' ))
101- ->content (new HtmlString ('
102- <button type="button"
103- wire:click="filter" wire:loading.attr="disabled"
104- class="bg-primary-500 px-3 py-2 text-white rounded hover:bg-primary-600
105- disabled:bg-primary-300">
106- ' . __ ('Filter ' ) . '
107- </button>
108- <button type="button"
109- wire:click="resetFilters" wire:loading.attr="disabled"
110- class="ml-2 bg-gray-800 px-3 py-2 text-white rounded hover:bg-gray-900
111- disabled:bg-gray-300">
112- ' . __ ('Reset filters ' ) . '
113- </button>
114- ' )),
115- ]),
116- ];
117- }
118-
11946 protected function getActions (): array
12047 {
12148 return [
@@ -130,141 +57,14 @@ protected function getActions(): array
13057 ];
13158 }
13259
133- protected static function getNavigationGroup (): ?string
134- {
135- return __ ('Management ' );
136- }
137-
13860 protected function getHeading (): string |Htmlable
13961 {
140- $ heading = '<div class="flex flex-col gap-1"> ' ;
141- $ heading .= '<span> ' . __ ('Kanban ' );
142- if ($ this ->project ) {
143- $ heading .= ' - ' . $ this ->project ->name . '</span> ' ;
144- } else {
145- $ heading .= '</span><span class="text-xs text-gray-400"> '
146- . __ ('Only default statuses are listed when no projects selected ' )
147- . '</span> ' ;
148- }
149- $ heading .= '</div> ' ;
150- return new HtmlString ($ heading );
151- }
152-
153- public function getStatuses (): Collection
154- {
155- $ query = TicketStatus::query ();
156- if ($ this ->project && $ this ->project ->status_type === 'custom ' ) {
157- $ query ->where ('project_id ' , $ this ->project ->id );
158- } else {
159- $ query ->whereNull ('project_id ' );
160- }
161- return $ query ->orderBy ('order ' )
162- ->get ()
163- ->map (function ($ item ) {
164- $ query = Ticket::query ();
165- if ($ this ->project ) {
166- $ query ->where ('project_id ' , $ this ->project ->id );
167- }
168- $ query ->where ('status_id ' , $ item ->id );
169- return [
170- 'id ' => $ item ->id ,
171- 'title ' => $ item ->name ,
172- 'color ' => $ item ->color ,
173- 'size ' => $ query ->count (),
174- 'add_ticket ' => $ item ->is_default && auth ()->user ()->can ('Create ticket ' )
175- ];
176- });
177- }
178-
179- public function getRecords (): Collection
180- {
181- $ query = Ticket::query ();
182- $ query ->with (['project ' , 'owner ' , 'responsible ' , 'status ' , 'type ' , 'priority ' , 'epic ' ]);
183- if ($ this ->project ) {
184- $ query ->where ('project_id ' , $ this ->project ->id );
185- } else {
186- $ query ->whereHas ('project ' , fn ($ query ) => $ query ->where ('status_type ' , 'default ' ));
187- }
188- if (sizeof ($ this ->users )) {
189- $ query ->where (function ($ query ) {
190- return $ query ->whereIn ('owner_id ' , $ this ->users )
191- ->orWhereIn ('responsible_id ' , $ this ->users );
192- });
193- }
194- if (sizeof ($ this ->types )) {
195- $ query ->whereIn ('type_id ' , $ this ->types );
196- }
197- if (sizeof ($ this ->priorities )) {
198- $ query ->whereIn ('priority_id ' , $ this ->priorities );
199- }
200- if ($ this ->includeNotAffectedTickets ) {
201- $ query ->orWhereNull ('responsible_id ' );
202- }
203- $ query ->where (function ($ query ) {
204- return $ query ->where ('owner_id ' , auth ()->user ()->id )
205- ->orWhere ('responsible_id ' , auth ()->user ()->id )
206- ->orWhereHas ('project ' , function ($ query ) {
207- return $ query ->where ('owner_id ' , auth ()->user ()->id )
208- ->orWhereHas ('users ' , function ($ query ) {
209- return $ query ->where ('users.id ' , auth ()->user ()->id );
210- });
211- });
212- });
213- return $ query ->get ()
214- ->map (fn (Ticket $ item ) => [
215- 'id ' => $ item ->id ,
216- 'code ' => $ item ->code ,
217- 'title ' => $ item ->name ,
218- 'owner ' => $ item ->owner ,
219- 'type ' => $ item ->type ,
220- 'responsible ' => $ item ->responsible ,
221- 'project ' => $ item ->project ,
222- 'status ' => $ item ->status ->id ,
223- 'priority ' => $ item ->priority ,
224- 'epic ' => $ item ->epic ,
225- 'relations ' => $ item ->relations ,
226- 'totalLoggedHours ' => $ item ->totalLoggedSeconds ? $ item ->totalLoggedHours : null
227- ]);
228- }
229-
230- public function recordUpdated (int $ record , int $ newIndex , int $ newStatus ): void
231- {
232- $ ticket = Ticket::find ($ record );
233- if ($ ticket ) {
234- $ ticket ->order = $ newIndex ;
235- $ ticket ->status_id = $ newStatus ;
236- $ ticket ->save ();
237- Filament::notify ('success ' , __ ('Ticket updated ' ));
238- }
239- }
240-
241- public function isMultiProject (): bool
242- {
243- return $ this ->project === null ;
244- }
245-
246- public function filter (): void
247- {
248- $ this ->getRecords ();
62+ return $ this ->kanbanHeading ();
24963 }
25064
251- public function resetFilters (): void
252- {
253- $ this ->form ->fill ();
254- $ this ->filter ();
255- }
256-
257- public function createTicket (): void
258- {
259- $ this ->ticket = true ;
260- }
261-
262- public function closeTicketDialog (bool $ refresh ): void
65+ protected function getFormSchema (): array
26366 {
264- $ this ->ticket = false ;
265- if ($ refresh ) {
266- $ this ->filter ();
267- }
67+ return $ this ->formSchema ();
26868 }
26969
27070}
0 commit comments