digitall_it
digitall_it
FFilament
Created by LancelotGamer on 3/3/2023 in #❓┊help
Notification custom action
Follow this steps: 1. php artisan make:livewire GlobalEventsListener 2. open App\Providers\AppServiceProvider and add this code:
Filament::serving(function () {
Livewire::component('global-events-listener', GlobalEventsListener::class);
Filament::registerRenderHook(
'user-menu.start',
fn (): string => Blade::render("@livewire('global-events-listener')")
);
});
Filament::serving(function () {
Livewire::component('global-events-listener', GlobalEventsListener::class);
Filament::registerRenderHook(
'user-menu.start',
fn (): string => Blade::render("@livewire('global-events-listener')")
);
});
3. open App\Http\Livewire\GlobalEventsListener and add this code:
protected $listeners = [
'myEventName'
];

public function myEventName($data) {
// handle the action here
}
protected $listeners = [
'myEventName'
];

public function myEventName($data) {
// handle the action here
}
4. in your notification's action, emit the event like this ->emit('myEventName', ['my_eloquent_model_id' => $myId]) 5. Profit.
15 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
By the way, thank you very much for pointing me out to Alpine.data(). I'll check that out!
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
and it looks like it binds to any element that dispatches an input event, so it actually needs that element to perform its entangling. Am I missing out something? I am so sorry for tiring you with those questions, I wanted to learn more about Filament Form. I fear that I couldn't model the behavior I need just inlining all the javascript so I used a class.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
So I used the hidden input to bind the state of the object. Also, I've checked out alpine.js "Data Binding" chapter here https://laravel-livewire.com/docs/2.x/properties#data-binding
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
ExampleInput is very simplified, and of course it would not need a separate javascript class to hold its logic. But, coming back to StripeInput, there's a lot more involved there
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
in this minimal use case, I am passing the state in the instance. Then I am changing the state programmatically after 5 seconds. Should that reflect on the server side? If not, what I am not understanding? (please ignore the double root in component, this is a pseudocode example)
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
<script>
class ExampleInput {
state;

constructor(state) {
this.state = state;
console.log('constructor, state:', state);
setTimeout(this.test.bind(this), 5000);
}

test() {
this.state = 'executed test, changed state';
console.log('test, state:', this.state);
}
}
</script>

<x-dynamic-component
:component="$getFieldWrapperView()"
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-action="$getHintAction()"
:hint-color="$getHintColor()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
<div id="$getId()" x-data="new ExampleInput($wire.entangle('{{ $getStatePath() }}').defer)">
</div>
</x-dynamic-component>
<script>
class ExampleInput {
state;

constructor(state) {
this.state = state;
console.log('constructor, state:', state);
setTimeout(this.test.bind(this), 5000);
}

test() {
this.state = 'executed test, changed state';
console.log('test, state:', this.state);
}
}
</script>

<x-dynamic-component
:component="$getFieldWrapperView()"
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-action="$getHintAction()"
:hint-color="$getHintColor()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
<div id="$getId()" x-data="new ExampleInput($wire.entangle('{{ $getStatePath() }}').defer)">
</div>
</x-dynamic-component>
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
The idea is that I can create a JavaScript class instance and pass some sort of reference to the entanglement so I can use that to communicate, but it’s obvious I need to study alpine.js more.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
I also made an example input field to perform a minimal use case debug
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
I’ll look that up and report.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
That’s exactly the point that I am missing
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
Sorry to bother you but I think I need to document all my mental process. It appears evident I am not using the full potential of alpine.js.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
also I would like to point out:
async confirmCardSetup() {
try {
const {setupIntent, error} = await this.stripe.confirmCardSetup(this.clientSecret, {
payment_method: {
card: this.stripeCardElement,
},
});

if (error) {
console.log("Stripe error:", error);
this.cardErrors.textContent = error.message;
this.state = {...this.state, valid: false, error: error.message};
} else {
console.log("Stripe Setup Intent:", setupIntent);
this.state = {...this.state, valid: true, stripeSetupIntentId: setupIntent.id, error: null};
window.livewire.emit('stripePaymentSetupIntentAssigned', {stripeSetupIntentId: setupIntent.id});
}
} catch (error) {
console.error('Error in confirmCardSetup:', error);
this.cardErrors.textContent = 'Si è verificato un errore durante la conferma del metodo di pagamento. Riprova.';
this.state = {...this.state, valid: false, error: 'Errore durante la conferma del metodo di pagamento'};
}
}
async confirmCardSetup() {
try {
const {setupIntent, error} = await this.stripe.confirmCardSetup(this.clientSecret, {
payment_method: {
card: this.stripeCardElement,
},
});

if (error) {
console.log("Stripe error:", error);
this.cardErrors.textContent = error.message;
this.state = {...this.state, valid: false, error: error.message};
} else {
console.log("Stripe Setup Intent:", setupIntent);
this.state = {...this.state, valid: true, stripeSetupIntentId: setupIntent.id, error: null};
window.livewire.emit('stripePaymentSetupIntentAssigned', {stripeSetupIntentId: setupIntent.id});
}
} catch (error) {
console.error('Error in confirmCardSetup:', error);
this.cardErrors.textContent = 'Si è verificato un errore durante la conferma del metodo di pagamento. Riprova.';
this.state = {...this.state, valid: false, error: 'Errore durante la conferma del metodo di pagamento'};
}
}
seems like window.livewire.emit is not catched during submit, I would have preferred to have some extension points in place, but I may simply have made something wrong. this.state = {...this.state, as I said is the code that makes the entangling and also doesn't work. Reading the documentation it seems like there is a phase before submission and after validation, but I think I can only access it server side. Even if I could dispatch an event at that point, the server uses code that is sync, while the flow is async and the server would not "await" for the frontend to answer.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
ah, I miss Angular so much... and I hate Angular so much!!
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
the good news is that this double submit workflow also handles the SCA (3D Secure) because it makes it async by using await in the confirmCardSetup.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
ok now I feel like an hack. I managed to simply stop the propagation, then doing my magic, then clicking on the submit button again. Is this cheating? Can it be done better? I feel dirty.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
handleSubmit = async (event) => {
console.log('handleSubmit', event);
event.preventDefault();

if (!this.state.stripeSetupIntentId) {
console.log('handleSubmit: no stripeSetupIntentId, calling confirmCardSetup');
event.stopPropagation();
await this.confirmCardSetup();
this.form.querySelector('button[type="submit"]').click();
}
}
handleSubmit = async (event) => {
console.log('handleSubmit', event);
event.preventDefault();

if (!this.state.stripeSetupIntentId) {
console.log('handleSubmit: no stripeSetupIntentId, calling confirmCardSetup');
event.stopPropagation();
await this.confirmCardSetup();
this.form.querySelector('button[type="submit"]').click();
}
}
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
The checkbox on the top says "I don't have the credit card with me, I'll insert it later". It will send you an email and you can get back later to insert your credit card data for registration. As we are talking about a Filament Form, the sky is the limit and the credit card becomes a field just like any other! Unfortunately my specific use case is a bit more complicated because you don't directly pay, instead you register the card for future usage (just like you do on Amazon). But as releasing this as a plugin we can figure out the different modes. Also notice that I am using fake data and working on unsecured localhost via Sail, as I am using a test key.
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
42 replies
FFilament
Created by digitall_it on 4/6/2023 in #❓┊help
StripeInput
you can see I have those setters and getters on the javascript class so I can make this.state = {...this.state, valid: false, error: event.error.message}; and than I can
set state(value) {
try {
this.boundField.value = JSON.stringify(value);
console.log('set state', value);
this.boundField.dispatchEvent(new Event('input', {bubbles: true}));
} catch (e) {
console.error("Failed to set state:", e);
}
}
set state(value) {
try {
this.boundField.value = JSON.stringify(value);
console.log('set state', value);
this.boundField.dispatchEvent(new Event('input', {bubbles: true}));
} catch (e) {
console.error("Failed to set state:", e);
}
}
42 replies