Giter Club home page Giter Club logo

inertia-modal's Introduction

100,000+ contributions in the last year

inertia-modal's People

Contributors

dependabot[bot] avatar emargareten avatar flexponsive avatar github-actions[bot] avatar laserhybiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

inertia-modal's Issues

Pagination in Modal

Hi,

I have an issue with pagination in the modal.
On first render of the modal the data seems to be ok:
Screenshot 2024-03-07 at 14 28 09

But when I try to paginate, the request is ok, but the returned data is not ok:
Screenshot 2024-03-07 at 14 28 19

Error:
Screenshot 2024-03-07 at 14 28 31

Any idea how I can fix this? Thanks!

Enhancement: accept `Arrayable`'s in Inertia::modal()

A request for an enhancement: to also accept Arrayable types when passing the props to Inertia:modal().

Example:

public function edit(Request $request, ContactLog $contact_log)
{
    return Inertia::modal(
        'ContactLog/Edit/Modal',
        EditContactLogViewModel::make($contact_log)->toArray() // <-- currently requires explicit ->toArray()
    )->baseRoute('contact-logs.index');
}

This will make the behaviour more seamless and consistent, since Inertia::render() supports it; and also in general wherever things are returned or propagated down from controllers to responses (e.g., Eloquent API Resources).

I happened to stumble upon this as I was swapping a project from momentum-modal to this package, and everything was seamless except for this one aspect.

I can work on a PR for this when I find some time later, but wanted to note this down here for further discussion.

Thoughts?

Can't get the modal to open

I was trying to setup this package as I am planning to work with quite a few modals. I couldn't even get one to work. So far I am just getting an error saying

**App\Http\Controllers\Contact\Form::__invoke(): Return value must be of type Emargareten\InertiaModal\Modal, null returned**

I can't even tell what I am doing wrong. Below is my setup:

The modal component:

<script setup>
import { defineProps, watch } from 'vue'
import { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'
import { IconTrash } from '@tabler/icons-vue'
import { Link, router, useForm } from '@inertiajs/vue3'
import { useModal } from 'vendor/emargareten/inertia-modal'

const props = defineProps({
  contact: Object,
})

const { show, close, redirect } = useModal()

const form = useForm({
  title: props.contact?.title,
  description: props.contact?.description,
  redirectUrl: `/boards/${props.contact?.board_id}`,
})

watch(() => props.contact, (contact) => {
  if (contact) {
    form.title = contact.title
    form.description = contact.description
    form.redirectUrl = `/boards/${contact.board_id}`
  }
})

function closeModal() {
  router.get(route('boards.show', { board: props.contact.board_id }), {}, {
    preserveState: true,
  })
}

function onSubmit() {
  form.put(route('contacts.update', { contact: props.contact.id }))
}
</script>

<template>
  <TransitionRoot
    :show="show"
    as="template"
    appear
  >
    <Dialog
      as="div"
      class="relative z-10"
      @close="close"
    >
      <TransitionChild
        as="template"
        enter="duration-300 ease-out"
        enter-from="opacity-0"
        enter-to="opacity-100"
        leave="duration-200 ease-in"
        leave-from="opacity-100"
        leave-to="opacity-0"
        @after-leave="redirect"
      >
        <div class="fixed inset-0 bg-black bg-opacity-40" />
      </TransitionChild>

      <div class="fixed inset-0 overflow-y-auto">
        <div
          class="flex items-start justify-center min-h-full px-4 py-12 text-center"
        >
          <TransitionChild
            as="template"
            enter="duration-300 ease-out"
            enter-from="opacity-0 scale-95"
            enter-to="opacity-100 scale-100"
            leave="duration-200 ease-in"
            leave-from="opacity-100 scale-100"
            leave-to="opacity-0 scale-95"
          >
            <DialogPanel
              class="w-full max-w-3xl overflow-hidden text-left align-middle transition-all transform bg-white rounded-md shadow-xl"
            >
              <div class="flex flex-col sm:flex-row">
                <form
                  class="flex-1 p-5"
                  @submit.prevent="onSubmit"
                >
                  <div>
                    <label
                      class="sr-only"
                      for="title"
                    >Title</label>
                    <textarea
                      id="title"
                      v-model="form.title"
                      class="block w-full text-sm border-gray-300 rounded-md shadow-sm focus:border-blue-400 focus:ring-blue-400"
                      name="title"
                      rows="1"
                    />
                  </div>

                  <div class="mt-4">
                    <label
                      class="inline-block mb-1 text-sm font-semibold text-gray-700"
                      for="description"
                    >Description</label>
                    <textarea
                      id="description"
                      v-model="form.description"
                      class="block w-full text-sm border-gray-300 rounded-md shadow-sm focus:border-blue-400 focus:ring-blue-400"
                      name="description"
                      rows="4"
                    />
                  </div>

                  <div class="mt-2 space-x-2">
                    <button
                      class="px-4 py-2 text-sm font-medium text-white rounded-md shadow-sm bg-rose-600 hover:bg-rose-500 focus:ring-2 focus:ring-offset-2 focus:ring-rose-500 focus:outline-none"
                      type="submit"
                    >
                      Save contact
                    </button>
                    <button
                      class="px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:text-black focus:ring-2 focus:ring-offset-2 focus:ring-rose-500 focus:outline-none"
                      type="button"
                      @click="close"
                    >
                      Cancel
                    </button>
                  </div>
                </form>
                <div class="p-5 bg-gray-100 sm:w-48">
                  <h3 class="mb-2 text-xs font-semibold tracking-wide text-gray-500 uppercase">
                    Actions
                  </h3>
                  <Link
                    :href="`/cards/${card?.id}`"
                    method="delete"
                    as="button"
                    class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 rounded-md shadow-sm hover:bg-gray-300 focus:ring-2 focus:ring-offset-2 focus:ring-rose-500 focus:outline-none"
                  >
                    <IconTrash class="w-4 h-4 mr-1 -ml-1 shrink-0" />
                    <span>Delete contact</span>
                  </Link>
                </div>
              </div>
            </DialogPanel>
          </TransitionChild>
        </div>
      </div>
    </Dialog>
  </TransitionRoot>
</template>

The plugin setup in app.ts:

createInertiaApp({
  title: title => `${title} - ${appName}`,
  resolve: name => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob<DefineComponent>('./Pages/**/*.vue')),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(modal, {
        resolve: name => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob<DefineComponent>('./Pages/**/*.vue')),
      })
      .use(plugin)
      .use(ZiggyVue, Ziggy)
      .mount(el)
  },
  progress: {
    color: '#4B5563',
  },
})

my routes:

Route::group(
  ['middleware' => 'auth'], function () {

  Route::get(
    '/',
    \App\Http\Controllers\Contact\Index::class
  )->name('contacts.index');

  Route::get(
    '/favourites',
    \App\Http\Controllers\Contact\Index::class
  )->name('contacts.favourites');

  Route::get(
    '/lists',
    \App\Http\Controllers\Contact\Index::class
  )->name('contacts.lists');

  Route::get(
    '/deleted',
    \App\Http\Controllers\Contact\Index::class
  )->name('contacts.deleted');

  Route::get(
    '/create',
    \App\Http\Controllers\Contact\Form::class
  )->name('contacts.create');

  Route::post(
    '/',
    \App\Http\Controllers\Contact\Store::class
  )->name('contacts.store');

  Route::patch(
    '/{contact}',
    \App\Http\Controllers\Contact\Update::class
  )->name('contacts.update');

  Route::delete(
    '/{contact}',
    \App\Http\Controllers\Contact\Trash::class
  )->name('contacts.destroy');

  Route::get(
    '/{contact:cid}',
    \App\Http\Controllers\Contact\Show::class
  )->name('contacts.show');

  Route::get(
    '/{contact:cid}/edit',
    \App\Http\Controllers\Contact\Form::class
  )->name('contacts.edit');

});

And lastly my controller is a self-invoking one:

<?php

namespace App\Http\Controllers\Contact;

use App\Http\Controllers\Controller;
use App\Models\Contact;
use Emargareten\InertiaModal\Modal;
use Inertia\Inertia;

class Form extends Controller
{
  public function __invoke(Contact $contact = null): Modal
  {
    return Inertia::modal('Contacts/ContactForm', [
      'contact' => $contact ?? new Contact(),
    ])->baseRoute('contacts.index');
  }
}

What could be the issue? On the other hand, please improve on the documentation so people can easily get started

Modal Routes Returning Plain JSON

So while I managed probably setup the inertia-modal to work, I am still facing an issue with the following error when I try to open a route in a modal. The error says,

All Inertia requests must receive a valid Inertia response, however a plain JSON response was received.
{"props":{"errors":[],"auth":{"user":{"id":1,"full_name":"Kingsley Motion"},"avatar":""},"ziggy":{"url":"http://kwik-leak.test","port":null,"defaults":[],"routes":{"sanctum.csrf-cookie":{"uri":"sanctum/csrf-cookie","methods":["GET","HEAD"]},"ignition.healthCheck":{"uri":"_ignition/health-check","methods":["GET","HEAD"]},"ignition.executeSolution":{"uri":"_ignition/execute-solution","methods":["POST"]},"ignition.updateConfig":{"uri":"_ignition/update-config","methods":["POST"]},"contacts.index":{"uri":"/","methods":["GET","HEAD"]},"contacts.favourites":{"uri":"favourites","methods":["GET","HEAD"]},"contacts.lists":{"uri":"lists","methods":["GET","HEAD"]},"contacts.deleted":{"uri":"deleted","methods":["GET","HEAD"]},"contacts.create":{"uri":"create","methods":["GET","HEAD"]},"contacts.store":{"uri":"/","methods":["POST"]},"contacts.update":{"uri":"{contact}","methods":["PATCH"],"bindings":{"contact":"id"}},"contacts.destroy":{"uri":"{contact}","methods":["DELETE"],"bindings":{"contact":"id"}},"contacts.show":{"uri":"{contact}","methods":["GET","HEAD"],"bindings":{"contact":"cid"}},"contacts.edit":{"uri":"{contact}/edit","methods":["GET","HEAD"],"bindings":{"contact":"cid"}},"phones.create":{"uri":"phones/{contact}/create","methods":["GET","HEAD"],"bindings":{"contact":"cid"}},"phones.store":{"uri":"phones/{contact}","methods":["POST"],"bindings":{"contact":"cid"}},"phones.edit":{"uri":"phones/{contact}/edit","methods":["GET","HEAD"],"bindings":{"contact":"cid"}},"phones.update":{"uri":"phones/{contact}","methods":["PATCH"],"bindings":{"contact":"cid"}},"phones.destroy":{"uri":"phones/{contact}/{phone}","methods":["DELETE"],"bindings":{"contact":"cid","phone":"id"}},"companies.create":{"uri":"companies/{contact}/create","methods":["GET","HEAD"],"bindings":{"contact":"cid"}},"companies.store":{"uri":"companies/{contact}","methods":["POST"],"bindings":{"contact":"cid"}},"companies.edit":{"uri":"companies/{contact}/edit","methods":["GET","HEAD"],"bindings":{"contact":"cid"}},"companies.update":{"uri":"companies/{contact}","methods":["PATCH"],"bindings":{"contact":"cid"}},"companies.destroy":{"uri":"companies/{company}","methods":["DELETE"],"bindings":{"company":"compid"}},"categories.create":{"uri":"categories/create/{company?}","methods":["GET","HEAD"]},"categories.store":{"uri":"categories","methods":["POST"]},"categories.edit":{"uri":"categories/edit","methods":["GET","HEAD"]},"categories.update":{"uri":"categories/{category}","methods":["PATCH"],"bindings":{"category":"gid"}},"categories.destroy":{"uri":"categories/{category}","methods":["DELETE"],"bindings":{"category":"gid"}},"broad.search":{"uri":"search/{term?}","methods":["GET","HEAD"]},"mail.compose":{"uri":"{contact}/compose-mail","methods":["GET","HEAD"],"bindings":{"contact":"id"}},"mail.send":{"uri":"{contact}/send-mail","methods":["POST"],"bindings":{"contact":"id"}},"profile.edit":{"uri":"profile","methods":["GET","HEAD"]},"profile.update":{"uri":"profile","methods":["PATCH"]},"profile.destroy":{"uri":"profile","methods":["DELETE"]},"register":{"uri":"register","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"password.request":{"uri":"forgot-password","methods":["GET","HEAD"]},"password.email":{"uri":"forgot-password","methods":["POST"]},"password.reset":{"uri":"reset-password/{token}","methods":["GET","HEAD"]},"password.store":{"uri":"reset-password","methods":["POST"]},"verification.notice":{"uri":"verify-email","methods":["GET","HEAD"]},"verification.verify":{"uri":"verify-email/{id}/{hash}","methods":["GET","HEAD"]},"verification.send":{"uri":"email/verification-notification","methods":["POST"]},"password.confirm":{"uri":"confirm-password","methods":["GET","HEAD"]},"password.update":{"uri":"password","methods":["PUT"]},"logout":{"uri":"logout","methods":["POST"]}},"location":"http://kwik-leak.test/create"},"toast":null,"modal":{"component":"Contacts/ContactForm","redirectURL":"http://kwik-leak.test/","props":{"contact":[]},"key":"8c140d82-7ad6-4327-bd2b-8575a9a71bf2"}},"url":"/create","version":"45652216da2731bf5ea55c3ee2f75ec7"}

Why is the response a plain JSON? Is this intentional or do I need to adjust the headers manually?

HTTP Resources - Inconsistent data wrapping

I have a route, /users/5/edit and pass the user into the modal as a prop using an HTTP Resource.

public function __invoke(User $user)
{
    return Inertia::modal('User/Edit')
        ->with([
            'user' => new UserResource($user),
        ])
        ->baseRoute('users.index');
}

If I go directly to the user edit page the user is wrapped in a "data" property. However, if I create an inertia link to open the user edit modal the user data is NOT wrapped in the data property.

When trying to figure out the problem myself I noticed the renderModal function in Emargareten\InertiaModal\Modal is only used when I click the inertia link and is not fired when I go directly to the edit user page.

Erroneous Redirect Causing Infinite Modal Loop Description

Hi, first thanks for the useful package!

I encountered the following issue:
If the modal is closed and reopened before the URL adjusts to the redirect route, the redirect function targets the modal resulting in a cyclic redirect issue, thus the modal cannot be closed.

All Inertia requests must receive a valid Inertia response, however a plain JSON response was received.

Hello,

i have a problem when i try to execute an action from an inertia modal. The message that i get is as follows All Inertia requests must receive a valid Inertia response, however a plain JSON response was received..

What i do is i have a sweet alert where if a user presses okay i execute the action in the controller.
Vue:

const startTicket = () => {
    let form = useForm({
        status: IN_PROGRESS,
        text: "ЗАПОЧНАТО!",
    });
    Swal.fire({
        title: "Започни тикет",
        text: "Дали сте сигурен дека сакате да го започнете тикетот?",
    }).then((result) => {
        if (result.isConfirmed) {
            form.post(route("tickets.automated_comments.store", props.ticket));
        }
    });
};

Route:

 Route::post('/automated-comments', [TicketCommentController::class, 'storeAutomatedComment'])->name('automated_comments.store');

Controller function:

public function storeAutomatedComment(Ticket $ticket, TicketCommentRequest $request): RedirectResponse
    {
        $validated = $request->validated();
        (new TicketService())->createTicketComment($ticket, $validated['text']);
        $ticket->status = $request->status;
        $ticket->save();

        return redirect()->route('vehicles.tickets.show', [$ticket->vehicle_id, $ticket->id]);
    }

Please support inertia-laravel v1.0

Hi!

The current version 0.6.x does not support inertia-laravel v1.0. The Inertia version is required for Laravel 11 support.

Thanks very much for your work in this library.

Behaviour of router.reload() inside a modal

Some context: I'm working with a project that was swapped from momentum-modal to this package.

I have a modal, which has an action within it that performs a task and then triggers a router.reload:

router.reload({
    preserveScroll: true,
    replace: true,
});

In other words, the current URL (a modal route) is being reloaded. When this happens, I'm not seeing expected reloaded data reflected in the modal even though the router.reload network request is made.

What I have to do now is:

router.reload({
    preserveScroll: true,
    replace: true,
    onSuccess: () => {
        // Explicitly update things that need to be updated
        form.something = props.payload.something;
    },
});

I tried playing with preserveState, no difference whether true or false.

Is this a bug, or is there something else I'm missing or misunderstanding about how reloads work?

What's different from momentum-modal?

Sorry I don't know where else to contact you. I'm wondering of the improvements your packages makes compared to momentum-modal from which it is inspired?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.