components([ Grid::make() ->schema([ /* |-------------------------------------------------------------------------- | Colonne principale |-------------------------------------------------------------------------- */ Grid::make(1) ->schema([ Tabs::make('MemberTabs') ->tabs([ /* |-------------------------------------------------------------------------- | TAB : Informations générales |-------------------------------------------------------------------------- */ Tabs\Tab::make(__('members.tabs.general_info')) ->icon(Heroicon::OutlinedInformationCircle) ->schema([ Section::make(__('members.sections.personal_info')) ->collapsible() ->schema([ TextInput::make('lastname') ->label(Member::getAttributeLabel('lastname')) ->required(), TextInput::make('firstname') ->label(Member::getAttributeLabel('firstname')) ->required(), DatePicker::make('date_of_birth') ->label(Member::getAttributeLabel('date_of_birth')), TextInput::make('company') ->label(Member::getAttributeLabel('company')), ]) ->columns(2), Section::make(__('members.sections.administrative_info')) ->collapsible() ->schema([ TextInput::make('keycloak_id') ->label(Member::getAttributeLabel('keycloak_id')), Select::make('nature') ->label(Member::getAttributeLabel('nature')) ->options([ 'physical' => Member::getAttributeLabel('physical'), 'legal' => Member::getAttributeLabel('legal'), ]) ->default('physical') ->required(), Select::make('group_id') ->label(Member::getAttributeLabel('group_id')) ->relationship('group', 'name') ->default(null), ]) ->columns(2), Section::make(__('members.sections.contact_info')) ->collapsible() ->schema([ TextInput::make('email') ->label(Member::getAttributeLabel('email')) ->email() ->required(), TextInput::make('phone1') ->label(Member::getAttributeLabel('phone1')) ->tel(), TextInput::make('phone2') ->label(Member::getAttributeLabel('phone2')) ->tel(), TextInput::make('address') ->label(Member::getAttributeLabel('address')), TextInput::make('zipcode') ->label(Member::getAttributeLabel('zipcode')), TextInput::make('city') ->label(Member::getAttributeLabel('city')), TextInput::make('country') ->label(Member::getAttributeLabel('country')), ]) ->columns(2), ]), /* |-------------------------------------------------------------------------- | TAB : Services/Modules |-------------------------------------------------------------------------- */ Tabs\Tab::make(__('members.tabs.modules')) ->icon(Heroicon::OutlinedPuzzlePiece) ->schema([ Section::make(__('members.sections.ispconfig_mail')) ->afterHeader([ ServiceToggleAction::forService('mail'), ]) ->collapsible() ->schema([ RepeatableEntry::make('ispconfig_mails') ->label(__('members.ispconfig.mail_data')) ->state(fn (?Member $record) => $record?->ispconfigs() ->where('type', IspconfigType::MAIL) ->get() ) ->schema([ TextEntry::make('email') ->label(__('members.ispconfig.email')), TextEntry::make('ispconfig_service_user_id') ->label(__('members.ispconfig.id')), TextEntry::make('data.mailuser.quota') ->label(__('members.ispconfig.quota')), TextEntry::make('data.mailuser.domain') ->label(__('members.ispconfig.domain')) ->default('retzien.fr'), ViewEntry::make('data') ->label('JSON') ->view('filament.components.json-viewer') ->viewData(fn ($state) => [ 'data' => $state, ]) ->columnSpanFull(), ]) ->columns(2), ]) ->visible(fn (?Member $record) => $record?->ispconfigs() ->where('type', IspconfigType::MAIL) ->exists() ), Section::make(__('members.sections.ispconfig_web')) ->afterHeader([ ServiceToggleAction::forService('webhosting'), ]) ->collapsible() ->schema([ RepeatableEntry::make('ispconfigs_web') ->label(__('members.ispconfig.web_data')) ->state(fn (?Member $record) => $record?->ispconfigs() ->where('type', IspconfigType::WEB) ->get() ->map(fn ($ispconfig) => $ispconfig->toArray()) ->all() ) ->schema([ TextEntry::make('data.domain_id') ->label(__('members.ispconfig.id')), TextEntry::make('data.domain') ->label(__('members.ispconfig.domain')), TextEntry::make('data.active') ->label(__('members.ispconfig.state')) ->formatStateUsing(fn ($state) => $state === 'y' ? __('members.ispconfig.enabled') : __('members.ispconfig.disabled') ), ViewEntry::make('data') ->label('JSON') ->view('filament.components.json-viewer') ->viewData(fn ($state) => [ 'data' => $state, ]) ->columnSpanFull(), ]) ->columns(3), ]) ->visible(fn (?Member $record) => $record?->ispconfigs() ->where('type', IspconfigType::WEB) ->exists() ), Section::make(__('members.sections.nextcloud')) ->afterHeader([ ServiceToggleAction::forService('nextcloud'), ]) ->collapsible() ->schema([ RepeatableEntry::make('nextcloud_accounts') ->label(__('members.ispconfig.nextcloud_data')) ->state(fn (?Member $record) => $record?->nextcloudAccounts() ->get() ->map(fn ($nextcloudAccount) => $nextcloudAccount->toArray()) ->all() ) ->schema([ TextEntry::make('nextcloud_user_id') ->label(__('members.ispconfig.nextcloud_id')), TextEntry::make('data.displayname') ->label(__('members.ispconfig.display_name')), TextEntry::make('data.enabled') ->label(__('members.ispconfig.state')) ->formatStateUsing(fn ($state) => $state == 'true' ? __('members.ispconfig.enabled') : __('members.ispconfig.disabled') ), ViewEntry::make('data') ->label('JSON') ->view('filament.components.json-viewer') ->viewData(fn ($state) => [ 'data' => $state, ]) ->columnSpanFull(), ]) ->columns(3), ]) ->visible(fn (?Member $record) => $record?->nextcloudAccounts() ->exists() ), ]), ]) ->contained(false), ]) ->columnSpan(3), /* |-------------------------------------------------------------------------- | Colonne latérale |-------------------------------------------------------------------------- */ Grid::make(1) ->schema([ Section::make(__('members.sections.status')) ->collapsible() ->schema([ Select::make('status') ->label(Member::getAttributeLabel('status')) ->options([ 'draft' => Member::getAttributeLabel('draft'), 'valid' => Member::getAttributeLabel('valid'), 'pending' => Member::getAttributeLabel('pending'), 'cancelled' => Member::getAttributeLabel('cancelled'), 'excluded' => Member::getAttributeLabel('excluded'), ]) ->default('draft') ->required(), Toggle::make('public_membership') ->label(Member::getAttributeLabel('public_membership')) ->required(), ]) ->extraAttributes(['class' => 'sticky top-4 h-fit']), Section::make(__('members.sections.actions')) ->collapsible() ->schema([ Action::make('send-payment-mail') ->label(__('members.actions.send_payment_mail')) ->icon('heroicon-o-envelope') ->action(function () { // Mail de paiement pour nouvelle inscription (Job) }), Action::make('send-renewal-mail') ->label(__('members.actions.send_renewal_mail')) ->icon('heroicon-o-envelope') ->action(function () { // Mail de relance à créer (Job) }), ]) ->extraAttributes(['class' => 'sticky top-4 h-fit']), ]) ->columnSpan(1), ]) ->columns(4) ->columnSpanFull(), ]); } }