88use App \Models \Plugin ;
99use App \Notifications \PluginSubmitted ;
1010use App \Services \GitHubUserService ;
11+ use Flux \Flux ;
1112use Illuminate \Support \Facades \Storage ;
13+ use Illuminate \Validation \ValidationException ;
1214use Livewire \Attributes \Computed ;
1315use Livewire \Attributes \Layout ;
1416use Livewire \Attributes \Title ;
@@ -74,45 +76,86 @@ public function mount(string $vendor, string $package): void
7476 $ this ->tier = $ this ->plugin ->tier ?->value;
7577 }
7678
79+ public function runPreflightChecks (): void
80+ {
81+ if (! $ this ->plugin ->isDraft ()) {
82+ return ;
83+ }
84+
85+ $ user = auth ()->user ();
86+ $ repoInfo = $ this ->plugin ->getRepositoryOwnerAndName ();
87+
88+ // Ensure a webhook secret exists so we can always show setup instructions
89+ if (! $ this ->plugin ->webhook_secret ) {
90+ $ this ->plugin ->generateWebhookSecret ();
91+ }
92+
93+ // Verify or install webhook
94+ if ($ repoInfo && $ user ->hasGitHubToken ()) {
95+ $ githubService = GitHubUserService::for ($ user );
96+ $ webhookUrl = $ this ->plugin ->getWebhookUrl ();
97+
98+ // Check if our webhook already exists on the repo
99+ if ($ webhookUrl && $ githubService ->webhookExists ($ repoInfo ['owner ' ], $ repoInfo ['repo ' ], $ webhookUrl )) {
100+ if (! $ this ->plugin ->webhook_installed ) {
101+ $ this ->plugin ->update (['webhook_installed ' => true ]);
102+ }
103+ } else {
104+ // Webhook not found on GitHub — try to create it
105+ $ webhookSecret = $ this ->plugin ->webhook_secret ?? $ this ->plugin ->generateWebhookSecret ();
106+ $ webhookResult = $ githubService ->createWebhook (
107+ $ repoInfo ['owner ' ],
108+ $ repoInfo ['repo ' ],
109+ $ this ->plugin ->getWebhookUrl (),
110+ $ webhookSecret
111+ );
112+ $ this ->plugin ->update (['webhook_installed ' => $ webhookResult ['success ' ]]);
113+ }
114+ }
115+
116+ // Run review checks
117+ (new ReviewPluginRepository ($ this ->plugin ))->handle ();
118+
119+ $ this ->plugin ->refresh ();
120+ }
121+
77122 public function submitForReview (): void
78123 {
79124 if (! $ this ->plugin ->isDraft ()) {
80- session ()->flash ('error ' , 'Only draft plugins can be submitted for review. ' );
125+ Flux::toast (variant: 'danger ' , text: 'Only draft plugins can be submitted for review. ' );
126+
127+ return ;
128+ }
129+
130+ if (! $ this ->plugin ->description ) {
131+ Flux::toast (variant: 'danger ' , text: 'Please add a description before submitting for review. ' );
81132
82133 return ;
83134 }
84135
85136 if (! $ this ->plugin ->support_channel ) {
86- session ()-> flash ( ' error ' , 'Please set a support channel before submitting for review. ' );
137+ Flux:: toast (variant: ' danger ' , text: 'Please set a support channel before submitting for review. ' );
87138
88139 return ;
89140 }
90141
91142 if ($ this ->plugin ->isPaid () && ! $ this ->plugin ->tier ) {
92- session ()-> flash ( ' error ' , 'Please select a pricing tier for your paid plugin. ' );
143+ Flux:: toast (variant: ' danger ' , text: 'Please select a pricing tier for your paid plugin. ' );
93144
94145 return ;
95146 }
96147
97- $ user = auth ()->user ();
148+ // Run preflight checks
149+ $ this ->runPreflightChecks ();
98150
99- // Install webhook
100- $ repoInfo = $ this ->plugin ->getRepositoryOwnerAndName ();
151+ // Only submit if required checks pass
152+ if (! $ this ->plugin ->passesRequiredReviewChecks ()) {
153+ Flux::toast (variant: 'danger ' , text: 'Your plugin doesn \'t pass all required checks yet. Please resolve the failing checks and try again. ' );
101154
102- if ($ repoInfo && $ user ->hasGitHubToken ()) {
103- $ webhookSecret = $ this ->plugin ->webhook_secret ?? $ this ->plugin ->generateWebhookSecret ();
104- $ githubService = GitHubUserService::for ($ user );
105- $ webhookResult = $ githubService ->createWebhook (
106- $ repoInfo ['owner ' ],
107- $ repoInfo ['repo ' ],
108- $ this ->plugin ->getWebhookUrl (),
109- $ webhookSecret
110- );
111- $ this ->plugin ->update (['webhook_installed ' => $ webhookResult ['success ' ]]);
155+ return ;
112156 }
113157
114- // Run review checks
115- (new ReviewPluginRepository ($ this ->plugin ))->handle ();
158+ $ user = auth ()->user ();
116159
117160 // Submit
118161 $ this ->plugin ->submit ();
@@ -121,41 +164,89 @@ public function submitForReview(): void
121164 // Notify
122165 $ user ->notify (new PluginSubmitted ($ this ->plugin ));
123166
124- session ()->flash ('success ' , 'Your plugin has been submitted for review! ' );
167+ Flux::toast (variant: 'success ' , text: 'Your plugin has been submitted for review! ' );
168+ }
169+
170+ public function certifyWebhook (): void
171+ {
172+ if ($ this ->plugin ->webhook_installed ) {
173+ return ;
174+ }
175+
176+ if (! $ this ->plugin ->webhook_secret ) {
177+ $ this ->plugin ->generateWebhookSecret ();
178+ }
179+
180+ $ this ->plugin ->update (['webhook_installed ' => true ]);
181+ $ this ->plugin ->refresh ();
182+
183+ $ this ->modal ('certify-webhook ' )->close ();
184+
185+ Flux::toast (variant: 'success ' , text: 'Webhook marked as installed. ' );
186+ }
187+
188+ public function retryWebhook (): void
189+ {
190+ $ user = auth ()->user ();
191+ $ repoInfo = $ this ->plugin ->getRepositoryOwnerAndName ();
192+
193+ if (! $ repoInfo || ! $ user ->hasGitHubToken ()) {
194+ Flux::toast (variant: 'danger ' , text: 'Unable to register webhook automatically. Please ensure your GitHub account is connected and the repository URL is valid. ' );
195+
196+ return ;
197+ }
198+
199+ $ webhookSecret = $ this ->plugin ->webhook_secret ?? $ this ->plugin ->generateWebhookSecret ();
200+ $ githubService = GitHubUserService::for ($ user );
201+ $ webhookResult = $ githubService ->createWebhook (
202+ $ repoInfo ['owner ' ],
203+ $ repoInfo ['repo ' ],
204+ $ this ->plugin ->getWebhookUrl (),
205+ $ webhookSecret
206+ );
207+
208+ $ this ->plugin ->update (['webhook_installed ' => $ webhookResult ['success ' ]]);
209+ $ this ->plugin ->refresh ();
210+
211+ if ($ webhookResult ['success ' ]) {
212+ Flux::toast (variant: 'success ' , text: 'Webhook installed successfully. ' );
213+ } else {
214+ Flux::toast (variant: 'danger ' , text: 'Failed to install webhook: ' .($ webhookResult ['error ' ] ?? 'Unknown error ' ));
215+ }
125216 }
126217
127218 public function withdrawFromReview (): void
128219 {
129220 if (! $ this ->plugin ->isPending ()) {
130- session ()-> flash ( ' error ' , 'Only pending plugins can be withdrawn. ' );
221+ Flux:: toast (variant: ' danger ' , text: 'Only pending plugins can be withdrawn. ' );
131222
132223 return ;
133224 }
134225
135226 $ this ->plugin ->withdraw ();
136227 $ this ->plugin ->refresh ();
137228
138- session ()-> flash ( 'success ' , 'Your plugin has been withdrawn from review and returned to draft. ' );
229+ Flux:: toast (variant: 'success ' , text: 'Your plugin has been withdrawn from review and returned to draft. ' );
139230 }
140231
141232 public function returnToDraft (): void
142233 {
143234 if (! $ this ->plugin ->isRejected ()) {
144- session ()-> flash ( ' error ' , 'Only rejected plugins can be returned to draft. ' );
235+ Flux:: toast (variant: ' danger ' , text: 'Only rejected plugins can be returned to draft. ' );
145236
146237 return ;
147238 }
148239
149240 $ this ->plugin ->returnToDraft ();
150241 $ this ->plugin ->refresh ();
151242
152- session ()-> flash ( 'success ' , 'Your plugin has been returned to draft. You can make changes and resubmit. ' );
243+ Flux:: toast (variant: 'success ' , text: 'Your plugin has been returned to draft. You can make changes and resubmit. ' );
153244 }
154245
155246 public function save (): void
156247 {
157248 if (! $ this ->plugin ->isDraft () && ! $ this ->plugin ->isApproved ()) {
158- session ()-> flash ( ' error ' , 'You can only edit draft or approved plugins. ' );
249+ Flux:: toast (variant: ' danger ' , text: 'You can only edit draft or approved plugins. ' );
159250
160251 return ;
161252 }
@@ -189,7 +280,7 @@ function (string $attribute, mixed $value, \Closure $fail) {
189280 ]);
190281
191282 if ($ this ->plugin ->isDraft () && $ this ->pluginType === 'paid ' && ! $ this ->hasCompletedDeveloperOnboarding ) {
192- session ()-> flash ( ' error ' , 'You must complete developer onboarding before setting a plugin as paid. ' );
283+ Flux:: toast (variant: ' danger ' , text: 'You must complete developer onboarding before setting a plugin as paid. ' );
193284
194285 return ;
195286 }
@@ -211,13 +302,13 @@ function (string $attribute, mixed $value, \Closure $fail) {
211302 $ this ->plugin ->updateDescription ($ this ->description , auth ()->id ());
212303 $ this ->plugin ->refresh ();
213304
214- session ()-> flash ( 'success ' , 'Plugin details saved successfully! ' );
305+ Flux:: toast (variant: 'success ' , text: 'Plugin details saved successfully! ' );
215306 }
216307
217308 public function updateIcon (): void
218309 {
219310 if (! $ this ->plugin ->isDraft () && ! $ this ->plugin ->isApproved ()) {
220- session ()-> flash ( ' error ' , 'You can only edit the icon for draft or approved plugins. ' );
311+ Flux:: toast (variant: ' danger ' , text: 'You can only edit the icon for draft or approved plugins. ' );
221312
222313 return ;
223314 }
@@ -239,13 +330,13 @@ public function updateIcon(): void
239330
240331 $ this ->plugin ->refresh ();
241332
242- session ()-> flash ( 'success ' , 'Plugin icon updated successfully! ' );
333+ Flux:: toast (variant: 'success ' , text: 'Plugin icon updated successfully! ' );
243334 }
244335
245336 public function uploadLogo (): void
246337 {
247338 if (! $ this ->plugin ->isDraft () && ! $ this ->plugin ->isApproved ()) {
248- session ()-> flash ( ' error ' , 'You can only upload a logo for draft or approved plugins. ' );
339+ Flux:: toast (variant: ' danger ' , text: 'You can only upload a logo for draft or approved plugins. ' );
249340
250341 return ;
251342 }
@@ -270,13 +361,13 @@ public function uploadLogo(): void
270361 $ this ->logo = null ;
271362 $ this ->iconMode = 'upload ' ;
272363
273- session ()-> flash ( 'success ' , 'Plugin logo updated successfully! ' );
364+ Flux:: toast (variant: 'success ' , text: 'Plugin logo updated successfully! ' );
274365 }
275366
276367 public function deleteIcon (): void
277368 {
278369 if (! $ this ->plugin ->isDraft () && ! $ this ->plugin ->isApproved ()) {
279- session ()-> flash ( ' error ' , 'You can only remove the icon for draft or approved plugins. ' );
370+ Flux:: toast (variant: ' danger ' , text: 'You can only remove the icon for draft or approved plugins. ' );
280371
281372 return ;
282373 }
@@ -294,13 +385,13 @@ public function deleteIcon(): void
294385 $ this ->plugin ->refresh ();
295386 $ this ->iconMode = 'gradient ' ;
296387
297- session ()-> flash ( 'success ' , 'Plugin icon removed successfully! ' );
388+ Flux:: toast (variant: 'success ' , text: 'Plugin icon removed successfully! ' );
298389 }
299390
300391 public function toggleListing (): void
301392 {
302393 if (! $ this ->plugin ->isApproved ()) {
303- session ()-> flash ( ' error ' , 'Only approved plugins can be listed or de-listed. ' );
394+ Flux:: toast (variant: ' danger ' , text: 'Only approved plugins can be listed or de-listed. ' );
304395
305396 return ;
306397 }
@@ -312,7 +403,18 @@ public function toggleListing(): void
312403 $ this ->plugin ->refresh ();
313404
314405 $ action = $ this ->plugin ->is_active ? 'listed ' : 'de-listed ' ;
315- session ()->flash ('success ' , "Your plugin has been {$ action }. " );
406+ Flux::toast (variant: 'success ' , text: "Your plugin has been {$ action }. " );
407+ }
408+
409+ public function validate ($ rules = [], $ messages = [], $ attributes = []): array
410+ {
411+ try {
412+ return parent ::validate ($ rules , $ messages , $ attributes );
413+ } catch (ValidationException $ e ) {
414+ $ this ->dispatch ('scroll-to-first-error ' );
415+
416+ throw $ e ;
417+ }
316418 }
317419
318420 public function render ()
0 commit comments