Skip to content

Conversation

@Boy132
Copy link
Member

@Boy132 Boy132 commented Aug 15, 2025

Also closes #1602

@Boy132 Boy132 self-assigned this Aug 15, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 15, 2025

📝 Walkthrough

Walkthrough

Adds a StartupVariable Filament form field (class, Blade view, enum) and replaces inline TextInput/Select logic in ServerResource CreateServer/EditServer with this field, centralizing type inference, select option parsing, and enabling live validation on blur.

Changes

Cohort / File(s) Summary
Startup Variable Enum
app/Enums/StartupVariableType.php
Added string-backed enum with cases Text = 'text', Select = 'select', Toggle = 'toggle'.
Form Field Class
app/Filament/Components/Forms/Fields/StartupVariable.php
Added StartupVariable Filament field exposing variable metadata, setters/getters, inference of input type from rules, parsing of select options, required detection, binding from form/record, and live validation-on-blur.
Blade View
resources/views/filament/components/startup-variable.blade.php
Added Blade view that renders a Select or Text input based on inferred type, binds state via Livewire, merges input attributes, and applies conditional classes/attributes.
Server Create Page Refactor
app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
Replaced per-variable TextInput/Select schema in server_variables repeater with StartupVariable, removed helper methods and unused imports, and hid the repeater label.
Server Edit Page Refactor
app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php
Replaced per-variable UI with StartupVariable::make(...)->fromRecord(), simplified mutateRelationshipDataBeforeSaveUsing to return normalized array, removed legacy helpers and unused imports, and hid the repeater label.

Sequence Diagram(s)

sequenceDiagram
  participant Admin as Admin User
  participant Form as Filament Form (Server Create/Edit)
  participant Field as StartupVariable Field
  participant Validator as Live Validation
  participant Livewire as Livewire State

  rect rgb(230,245,255)
    Admin->>Form: Open server variables step / interact with repeater
    Form->>Field: Render with variable metadata & rules
    Field->>Field: Infer type (text/select/toggle) and options from rules
  end

  rect rgb(235,255,230)
    Admin->>Field: Enter value and blur
    Field->>Validator: Trigger live validation (on blur)
    Validator-->>Field: Return errors or success
    Field->>Livewire: Update state and surface inline errors
  end

  rect rgb(255,243,230)
    Admin->>Form: Attempt Next Step / Submit
    Form->>Validator: Run step/submit validation (includes field rules)
    Validator-->>Form: Return errors or proceed
  end
Loading

Assessment against linked issues

Objective Addressed Explanation
Validate egg variables before final submit and highlight invalid fields (#1602)

Out-of-scope changes

Code Change Explanation
Added Toggle enum case and TODO comment (app/Enums/StartupVariableType.php) Introduces a toggle type and a TODO about Blade support; not required by #1602.
mutateRelationshipDataBeforeSaveUsing changed to return array (app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php) Data-shape normalization during save is unrelated to validation timing or inline highlighting requirements from #1602.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e7dc4bb and 90d1f8d.

📒 Files selected for processing (1)
  • resources/views/filament/components/startup-variable.blade.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • resources/views/filament/components/startup-variable.blade.php

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php (1)

328-337: Synchronize environment with edits to server_variables
The environment array is only initialized when the egg selection changes, so any user edits to individual variable values in the repeater aren’t reflected at submission time. Since ServerCreationService::handle() uses Arr::get($data, 'environment', []) to validate and store variables, stale values may be persisted.

Please update the repeater definition in:

  • app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php around the Repeater::make('server_variables') block (lines ~328–337)
  • And likewise in the edit page (lines 429–443)

Add the following to the repeater schema:

                                     ->hidden(fn ($state) => empty($state))
+                                    ->live()
+                                    ->afterStateUpdated(function ($state, Set $set) {
+                                        $env = collect($state ?? [])
+                                            ->filter(fn ($item) => isset($item['env_variable']))
+                                            ->mapWithKeys(fn ($item) => [
+                                                $item['env_variable'] => $item['variable_value'] ?? '',
+                                            ])
+                                            ->all();
+                                        $set('environment', $env);
+                                    })
                                     ->schema([
                                         StartupVariable::make('variable_value'),
                                     ])

This ensures that any adjustments to variable_value immediately update the environment payload.

🧹 Nitpick comments (4)
app/Enums/StartupVariableType.php (1)

5-10: Enum looks good; ensure Toggle UI is implemented or gated

The Toggle case is defined but currently not rendered in the Blade view. Either implement Toggle rendering in the Blade (preferred) or temporarily gate getType() from returning Toggle until the Blade supports it to avoid a confusing UX for boolean variables.

resources/views/filament/components/startup-variable.blade.php (2)

19-21: Add class handling for Toggle (or reuse select styling for now)

Right now classes only handle Text and Select. If you render Toggle as a select fallback, also treat Toggle as fi-fo-select for consistent styling; otherwise add a dedicated class for Toggle.

Apply this minimal change to include Toggle under the select styling:

-            'fi-fo-select' => $type === \App\Enums\StartupVariableType::Select
+            'fi-fo-select' => $type === \App\Enums\StartupVariableType::Select || $type === \App\Enums\StartupVariableType::Toggle

41-55: Accessibility: add aria-invalid on text input for clearer error signalling

You already set the wrapper’s valid state. Propagate aria-invalid down to the input for assistive tech.

For example:

             <x-filament::input
                 :attributes="
                     \Filament\Support\prepare_inherited_attributes($getExtraInputAttributeBag())
                         ->merge($getExtraAlpineAttributes(), escape: false)
                         ->merge([
                             'id' => $getId(),
                             'required' => $isRequired(),
                             'disabled' => $isDisabled,
                             'placeholder' => $getVariableDefault(),
                             $applyStateBindingModifiers('wire:model') => $statePath,
+                            'aria-invalid' => $errors->has($statePath),
                         ], escape: false)
                 "
             />
app/Filament/Components/Forms/Fields/StartupVariable.php (1)

140-145: Strict in_array for required rule (minor)

Use strict comparison to avoid edge cases with non-string rule values.

-        return in_array('required', $rules);
+        return in_array('required', $rules, true);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c77a37e and e6d42eb.

📒 Files selected for processing (5)
  • app/Enums/StartupVariableType.php (1 hunks)
  • app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php (3 hunks)
  • app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (3 hunks)
  • app/Filament/Components/Forms/Fields/StartupVariable.php (1 hunks)
  • resources/views/filament/components/startup-variable.blade.php (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
app/Filament/Components/Forms/Fields/StartupVariable.php (2)
app/Models/ServerVariable.php (2)
  • ServerVariable (20-64)
  • variable (60-63)
app/Filament/Server/Pages/ServerFormPage.php (1)
  • getRecord (66-72)
app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php (1)
app/Filament/Components/Forms/Fields/StartupVariable.php (1)
  • StartupVariable (14-178)
app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (1)
app/Filament/Components/Forms/Fields/StartupVariable.php (1)
  • StartupVariable (14-178)
🔇 Additional comments (4)
app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php (1)

430-443: Good refactor: centralized per-variable field with early validation behavior

Replacing the inline schema with StartupVariable::make('variable_value') simplifies the form and restores field-level validation via the component (->rules() and ->live(onBlur: true)). This aligns with the objective to validate on “Next Step”.

app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (2)

10-10: Import of StartupVariable is appropriate

The import is correct and matches the new field usage below.


617-644: Cleaner relationship save and field usage

  • Using mutateRelationshipDataBeforeSaveUsing to guarantee variable_value exists is concise and safe.
  • Replacing the dynamic inline schema with StartupVariable simplifies maintenance and keeps behavior consistent with CreateServer.
app/Filament/Components/Forms/Fields/StartupVariable.php (1)

39-52: Great: centralized rules and on-blur live validation

Setting rules and enabling live(onBlur: true) on the field directly is exactly what’s needed to restore early validation on step transition and blur.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
app/Filament/Components/Forms/Fields/StartupVariable.php (2)

148-161: Resolved: using Arr::first over array_first — LGTM

Thanks for switching to Illuminate\Support\Arr::first; this avoids reliance on helpers and aligns with framework APIs.


163-178: Bug: getSelectOptions doesn’t trim values (each() doesn’t transform) and Str::trim() is invalid

  • each() does not map/transform collection items; options remain untrimmed, leading to select/validation mismatches.
  • Str::trim() is not a valid static method; use PHP’s trim() or Stringable.

Apply this diff:

     $inRule = Arr::first($rules, fn ($value) => str($value)->startsWith('in:'));
     if ($inRule) {
         return str($inRule)
             ->after('in:')
             ->explode(',')
-            ->each(fn ($value) => Str::trim($value))
+            ->map(fn ($value) => trim($value))
             ->all();
     }
🧹 Nitpick comments (3)
app/Filament/Components/Forms/Fields/StartupVariable.php (3)

39-52: Polish UX: null-safe prefix, required asterisk, and default value wiring in setUp()

Tiny improvements to reduce noise and improve clarity:

  • Avoid showing "{{}}" when env is missing.
  • Show a required asterisk for required variables.
  • Pre-fill with the variable’s default when no state is set.
  • Don’t render a placeholder em-dash for empty helper text.

Apply this diff:

     $this->label(fn (StartupVariable $component) => $component->getVariableName());

-    $this->prefix(fn (StartupVariable $component) => '{{' . $component->getVariableEnv() . '}}');
+    $this->prefix(fn (StartupVariable $component) => ($env = $component->getVariableEnv()) ? '{{' . $env . '}}' : null);

     $this->hintIcon('tabler-code');

     $this->hintIconTooltip(fn (StartupVariable $component) => implode('|', $component->getVariableRules()));

-    $this->helperText(fn (StartupVariable $component) => !$component->getVariableDesc() ? '—' : $component->getVariableDesc());
+    $this->helperText(fn (StartupVariable $component) => $component->getVariableDesc() ?: null);

     $this->rules(fn (StartupVariable $component) => $component->getVariableRules());
+    $this->required(fn (StartupVariable $component) => $component->isRequired());
+    $this->default(fn (StartupVariable $component) => $component->getVariableDefault());

     $this->live(onBlur: true);

90-129: Guard against missing relation to avoid notices

If the ServerVariable->variable relation isn’t loaded or is null, these accessors can raise notices in edge cases. Null-safe access keeps things resilient.

Apply this diff:

-        if ($record instanceof ServerVariable) {
-            return $record->variable->name;
-        }
+        if ($record instanceof ServerVariable) {
+            return $record->variable?->name;
+        }
@@
-        if ($record instanceof ServerVariable) {
-            return $record->variable->description;
-        }
+        if ($record instanceof ServerVariable) {
+            return $record->variable?->description;
+        }
@@
-        if ($record instanceof ServerVariable) {
-            return $record->variable->env_variable;
-        }
+        if ($record instanceof ServerVariable) {
+            return $record->variable?->env_variable;
+        }
@@
-        if ($record instanceof ServerVariable) {
-            return $record->variable->default_value;
-        }
+        if ($record instanceof ServerVariable) {
+            return $record->variable?->default_value;
+        }

131-139: Defensive default for rules when relation is missing

Ensure rules always resolve to an array even if the relation is absent.

Apply this diff:

-        if ($record instanceof ServerVariable) {
-            return $record->variable->rules;
-        }
+        if ($record instanceof ServerVariable) {
+            return (array) ($record->variable?->rules ?? []);
+        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a9ecc1f and bf937c9.

📒 Files selected for processing (1)
  • app/Filament/Components/Forms/Fields/StartupVariable.php (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/Filament/Components/Forms/Fields/StartupVariable.php (2)
app/Models/ServerVariable.php (2)
  • ServerVariable (20-64)
  • variable (60-63)
app/Filament/Server/Pages/ServerFormPage.php (1)
  • getRecord (66-72)
🔇 Additional comments (2)
app/Filament/Components/Forms/Fields/StartupVariable.php (2)

51-51: Early on-blur validation meets #1602 — LGTM

Using live(onBlur: true) restores immediate validation feedback during the server creation flow. This aligns with the early validation objective.


156-158: Confirm Toggle rendering is supported in the Blade view

getType() can return Toggle, but ensure the Blade view renders a toggle input for this case; otherwise booleans will degrade to Text/Select.

If the toggle branch isn’t implemented yet in resources/views/filament/components/startup-variable.blade.php, I can propose the Blade changes to add it.

{
case Text = 'text';
case Select = 'select';
case Toggle = 'toggle'; // TODO: add toggle to blade view
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we wait for v4 since they made it a blade component ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating servers is also broken in v4, this probably fixes it in both

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating servers is also broken in v4, this probably fixes it in both

Yes.

@rmartinoscar
Copy link
Member

rmartinoscar commented Aug 21, 2025

I'd like the possibility of having a multiple rule and we should set this otherwise it will always use the first option when not selected instead of null.

$this->selectablePlaceholder(fn (StartupVariable $component) => !$component->isRequired())

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
app/Filament/Components/Forms/Fields/StartupVariable.php (1)

165-180: Trim options correctly; each() doesn’t transform values.

You previously received the same suggestion; it still appears here. Using each() won’t trim option strings. Switch to map() (or array_map) to avoid mismatches and display issues.

Apply this diff:

         $inRule = Arr::first($rules, fn ($value) => str($value)->startsWith('in:'));
         if ($inRule) {
             return str($inRule)
                 ->after('in:')
                 ->explode(',')
-                ->each(fn ($value) => Str::trim($value))
+                ->map(fn ($value) => trim($value))
                 ->all();
         }

If this was already fixed in another branch/commit, please rebase this file so the change lands here as well.

🧹 Nitpick comments (5)
app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (3)

618-618: Hidden label: consider screen-reader accessibility.

Hiding the label improves visual density but can hurt accessibility. Ensure the inner field wrapper or Blade view provides an aria-label or sr-only label so assistive tech still announces the variable name.

If you want, I can scan the Blade view to confirm an accessible label is rendered for each repeater item.


635-639: Normalize/trim values before save without masking booleans.

The null-coalescing assignment avoids nulls in the DB, which is good. We can harden this by trimming strings while preserving booleans and other scalar types.

Apply this diff:

-    ->mutateRelationshipDataBeforeSaveUsing(function (array $data): array {
-        $data['variable_value'] ??= '';
+    ->mutateRelationshipDataBeforeSaveUsing(function (array $data): array {
+        $value = $data['variable_value'] ?? '';
+        // Trim strings, leave non-strings as-is (e.g., boolean from Toggle type)
+        $data['variable_value'] = is_string($value) ? trim($value) : $value;

641-644: Expose a selectable placeholder when the variable is not required (prevents auto-selecting the first option).

Review feedback asked to avoid defaulting to the first option when the field is optional. Once the field supports a selectable placeholder flag, wire it here.

Apply this diff (assuming the field gains a selectablePlaceholder() API — see my comment in StartupVariable.php):

-    StartupVariable::make('variable_value')
-        ->fromRecord(),
+    StartupVariable::make('variable_value')
+        ->fromRecord()
+        ->selectablePlaceholder(fn (StartupVariable $component) => !$component->isRequired()),

I can also patch the Blade view to render a null/disabled placeholder option when this flag is true — say the word and I’ll include the snippet.

app/Filament/Components/Forms/Fields/StartupVariable.php (2)

143-148: Make required detection robust to rule variants.

Some eggs use conditional required rules (required_if, required_without, etc.). If you want the UI to treat those as “required-ish,” match any rule starting with “required”.

Apply this diff:

-        return in_array('required', $rules);
+        return collect($rules)->contains(fn (string $r) => str($r)->startsWith('required'));

If strict “required only” is intentional for UX reasons, feel free to skip this.


38-49: Tooltip readability nit: add spaces between rules.

Minor readability tweak: use " | " between rules instead of "|".

Apply this diff:

-        $this->hintIconTooltip(fn (StartupVariable $component) => implode('|', $component->getVariableRules()));
+        $this->hintIconTooltip(fn (StartupVariable $component) => implode(' | ', $component->getVariableRules()));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1f5c5a7 and d146b5e.

📒 Files selected for processing (3)
  • app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php (3 hunks)
  • app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (3 hunks)
  • app/Filament/Components/Forms/Fields/StartupVariable.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
🧰 Additional context used
🧬 Code graph analysis (2)
app/Filament/Components/Forms/Fields/StartupVariable.php (1)
app/Models/ServerVariable.php (2)
  • ServerVariable (20-64)
  • variable (60-63)
app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (1)
app/Filament/Components/Forms/Fields/StartupVariable.php (2)
  • StartupVariable (17-181)
  • fromRecord (70-79)
🔇 Additional comments (3)
app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php (1)

10-10: Good switch to the custom StartupVariable field.

Import looks correct and aligns this page with the new component used elsewhere in the PR.

app/Filament/Components/Forms/Fields/StartupVariable.php (2)

150-163: Type inference looks good; consider future-proofing boolean/enum cases.

Current checks are fine. If eggs emit boolean as “in:true,false” rather than “boolean”, getType() will still return Select (which is acceptable). Just a note that getSelectOptions() will then contain “true/false” strings and you’ll want to cast on dehydrate if you later store booleans.

Do you want me to add a small dehydrateStateUsing to coerce "true"/"false" strings to booleans only when getType() === Toggle?


59-79: fromForm()/fromRecord() APIs are solid.

The closures are typed and will evaluate correctly within a relationship repeater context. Good separation for Create vs Edit flows.

@Boy132 Boy132 merged commit 8f277aa into main Sep 2, 2025
25 checks passed
@Boy132 Boy132 deleted the boy132/refactor-startup-variables branch September 2, 2025 07:05
@github-actions github-actions bot locked and limited conversation to collaborators Sep 2, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Egg Variables are not validated on server creation until final submit

4 participants