88use App \Models \PolydockAppInstance ;
99use App \PolydockEngine \Helpers \AmazeeAiBackendHelper ;
1010use App \PolydockEngine \Helpers \LagoonHelper ;
11+ use App \Services \PolydockAppClassDiscovery ;
1112use Filament \Forms ;
1213use Filament \Forms \Form ;
1314use Filament \Infolists \Infolist ;
1718use Filament \Tables \Columns \TextColumn ;
1819use Filament \Tables \Filters \SelectFilter ;
1920use Filament \Tables \Table ;
21+ use FreedomtechHosting \PolydockApp \Attributes \PolydockAppInstanceFields ;
2022use FreedomtechHosting \PolydockApp \Enums \PolydockAppInstanceStatus ;
2123
2224class PolydockAppInstanceResource extends Resource
@@ -70,20 +72,28 @@ public static function table(Table $table): Table
7072 TextColumn::make ('send_midtrial_email_at ' )
7173 ->label ('Midtrial Email ' )
7274 ->description (fn ($ record ) => $ record ->midtrial_email_sent ? 'Sent ' : 'Pending ' )
73- ->state (fn ($ record ) => $ record ->send_midtrial_email_at ? $ record ->send_midtrial_email_at ->format ('Y-m-d H:i:s ' ) : '' ),
75+ ->state (fn ($ record ) => $ record ->send_midtrial_email_at
76+ ? $ record ->send_midtrial_email_at ->format ('Y-m-d H:i:s ' )
77+ : '' ),
7478 TextColumn::make ('send_one_day_left_email_at ' )
7579 ->label ('1D Left Email ' )
7680 ->description (fn ($ record ) => $ record ->one_day_left_email_sent ? 'Sent ' : 'Pending ' )
77- ->state (fn ($ record ) => $ record ->send_one_day_left_email_at ? $ record ->send_one_day_left_email_at ->format ('Y-m-d H:i:s ' ) : '' ),
81+ ->state (fn ($ record ) => $ record ->send_one_day_left_email_at
82+ ? $ record ->send_one_day_left_email_at ->format ('Y-m-d H:i:s ' )
83+ : '' ),
7884 TextColumn::make ('trial_complete_email_sent ' )
7985 ->label ('Trial Complete Email ' )
80- ->state (fn ($ record ) => ($ record ->is_trial && $ record ->trial_complete_email_sent ) ? 'Sent ' : ($ record ->is_trial ? 'Pending ' : '' )),
86+ ->state (fn ($ record ) => $ record ->is_trial && $ record ->trial_complete_email_sent
87+ ? 'Sent '
88+ : ($ record ->is_trial ? 'Pending ' : '' )),
8189 ])
8290 ->filters ([
8391 SelectFilter::make ('status ' )
84- ->options (collect (PolydockAppInstanceStatus::cases ())
85- ->mapWithKeys (fn ($ status ) => [$ status ->value => $ status ->getLabel ()])
86- ->toArray ())
92+ ->options (
93+ collect (PolydockAppInstanceStatus::cases ())
94+ ->mapWithKeys (fn ($ status ) => [$ status ->value => $ status ->getLabel ()])
95+ ->toArray (),
96+ )
8797 ->multiple ()
8898 ->label ('Instance Status ' )
8999 ->indicator ('Status ' ),
@@ -165,7 +175,8 @@ public static function table(Table $table): Table
165175 ->actions ([
166176 Tables \Actions \ViewAction::make (),
167177 Tables \Actions \EditAction::make (),
168- ])->headerActions ([
178+ ])
179+ ->headerActions ([
169180 ExportAction::make ()
170181 ->label ('Export registrations ' )
171182 ->exporter (UserRemoteRegistrationExporter::class),
@@ -205,10 +216,19 @@ public static function infolist(Infolist $infolist): Infolist
205216 ->schema ([
206217 \Filament \Infolists \Components \TextEntry::make ('storeApp.lagoon_deploy_region_id_ext ' )
207218 ->label ('Deploy Region ' )
208- ->formatStateUsing (fn ($ state ) => LagoonHelper::getLagoonCodeDataValueForRegion ($ state , 'name ' )),
209- \Filament \Infolists \Components \TextEntry::make ('storeApp.amazee_ai_backend_region_id_ext ' )
219+ ->formatStateUsing (
220+ fn ($ state ) => LagoonHelper::getLagoonCodeDataValueForRegion ($ state , 'name ' ),
221+ ),
222+ \Filament \Infolists \Components \TextEntry::make (
223+ 'storeApp.amazee_ai_backend_region_id_ext ' ,
224+ )
210225 ->label ('AI Backend Region ' )
211- ->formatStateUsing (fn ($ state ) => AmazeeAiBackendHelper::getAmazeeAiBackendCodeDataValueForRegion ($ state , 'name ' )),
226+ ->formatStateUsing (
227+ fn ($ state ) => AmazeeAiBackendHelper::getAmazeeAiBackendCodeDataValueForRegion (
228+ $ state ,
229+ 'name ' ,
230+ ),
231+ ),
212232 ]),
213233 ])
214234 ->columnSpan (2 ),
@@ -229,6 +249,13 @@ public static function infolist(Infolist $infolist): Infolist
229249 ])
230250 ->columnSpan (1 ),
231251
252+ \Filament \Infolists \Components \Section::make ('Instance Configuration ' )
253+ ->description ('Instance-specific settings configured at creation. ' )
254+ ->schema (fn ($ record ) => self ::getRenderedInstanceConfigForRecord ($ record ))
255+ ->visible (fn ($ record ) => self ::hasInstanceConfigFields ($ record ))
256+ ->columnSpan (3 )
257+ ->collapsible (),
258+
232259 \Filament \Infolists \Components \Section::make ('Instance Data ' )
233260 ->description ('Safe data that can be shared with webhooks ' )
234261 ->schema (fn ($ record ) => self ::getRenderedSafeDataForRecord ($ record ))
@@ -249,7 +276,7 @@ public static function getRenderedSafeDataForRecord(PolydockAppInstance $record)
249276 }
250277
251278 if ($ value === null ) {
252- $ value = '' ;
279+ $ value = 'N/A ' ;
253280 }
254281
255282 $ renderKey = 'webhook_data_ ' .$ key ;
@@ -271,6 +298,76 @@ public static function getRenderedSafeDataForRecord(PolydockAppInstance $record)
271298 return $ renderedArray ;
272299 }
273300
301+ /**
302+ * Check if the record's app class defines instance configuration fields.
303+ */
304+ public static function hasInstanceConfigFields (PolydockAppInstance $ record ): bool
305+ {
306+ $ storeApp = $ record ->storeApp ;
307+ if (! $ storeApp || empty ($ storeApp ->polydock_app_class )) {
308+ return false ;
309+ }
310+
311+ $ discovery = app (PolydockAppClassDiscovery::class);
312+
313+ return ! empty ($ discovery ->getAppInstanceInfolistSchema ($ storeApp ->polydock_app_class ));
314+ }
315+
316+ /**
317+ * Get rendered infolist components for instance configuration fields.
318+ *
319+ * Values are loaded from PolydockVariables associated with the app instance.
320+ */
321+ public static function getRenderedInstanceConfigForRecord (PolydockAppInstance $ record ): array
322+ {
323+ $ storeApp = $ record ->storeApp ;
324+ if (! $ storeApp || empty ($ storeApp ->polydock_app_class )) {
325+ return [];
326+ }
327+
328+ $ discovery = app (PolydockAppClassDiscovery::class);
329+ $ fieldNames = $ discovery ->getAppInstanceFormFieldNames ($ storeApp ->polydock_app_class );
330+
331+ if (empty ($ fieldNames )) {
332+ return [];
333+ }
334+
335+ // Build a simple display of instance config values from PolydockVariables
336+ $ renderedArray = [];
337+ $ instanceConfigPrefix = PolydockAppInstanceFields::FIELD_PREFIX ;
338+
339+ foreach ($ fieldNames as $ fieldName ) {
340+ $ value = $ record ->getPolydockVariableValue ($ fieldName );
341+
342+ // Create a human-readable label from the field name
343+ // e.g., "instance_config_ai_model_override" -> "Ai Model Override"
344+ $ labelName = str_replace ($ instanceConfigPrefix , '' , $ fieldName );
345+ $ labelName = str_replace ('_ ' , ' ' , $ labelName );
346+ $ labelName = ucwords ($ labelName );
347+
348+ // Check if value should be masked (for encrypted fields)
349+ $ isEncrypted = $ record ->isPolydockVariableEncrypted ($ fieldName );
350+
351+ $ renderedItem = \Filament \Infolists \Components \TextEntry::make ('instance_config_display_ ' .$ fieldName )
352+ ->label ($ labelName );
353+
354+ if ($ isEncrypted && $ value !== null && $ value !== '' ) {
355+ // Mask encrypted values
356+ $ renderedItem ->state ('******** ' );
357+ } elseif ($ value === null || $ value === '' ) {
358+ $ renderedItem ->state ('Not configured ' )
359+ ->color ('gray ' );
360+ } else {
361+ $ renderedItem ->state ($ value );
362+ }
363+
364+ $ renderedArray [] = $ renderedItem ;
365+ }
366+
367+ return $ renderedArray ;
368+ }
369+
370+ #[\Override]
274371 public static function getRelations (): array
275372 {
276373 return [
@@ -281,14 +378,15 @@ public static function getRelations(): array
281378 #[\Override]
282379 public static function canCreate (): bool
283380 {
284- return false ;
381+ return true ;
285382 }
286383
287384 #[\Override]
288385 public static function getPages (): array
289386 {
290387 return [
291388 'index ' => Pages \ListPolydockAppInstances::route ('/ ' ),
389+ 'create ' => Pages \CreatePolydockAppInstance::route ('/create ' ),
292390 'view ' => Pages \ViewPolydockAppInstance::route ('/{record} ' ),
293391 'edit ' => Pages \EditPolydockAppInstance::route ('/{record}/edit ' ),
294392 ];
0 commit comments