work in progress: removed applayout from pages, trying to get broadcasting to work
This commit is contained in:
parent
16a6962149
commit
1d1ca88aea
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"laravel-boost": {
|
||||||
|
"command": "/usr/bin/php8.4",
|
||||||
|
"args": [
|
||||||
|
"/home/daan/sites/ledgerrz/artisan",
|
||||||
|
"boost:mcp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
214
.junie/skills/echo-development/SKILL.md
Normal file
214
.junie/skills/echo-development/SKILL.md
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
---
|
||||||
|
name: echo-development
|
||||||
|
description: "Develops real-time broadcasting with Laravel Echo. Activates when setting up broadcasting (Reverb, Pusher, Ably); creating ShouldBroadcast events; defining broadcast channels (public, private, presence, encrypted); authorizing channels; configuring Echo; listening for events; implementing client events (whisper); setting up model broadcasting; broadcasting notifications; or when the user mentions broadcasting, Echo, WebSockets, real-time events, Reverb, or presence channels."
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
author: laravel
|
||||||
|
---
|
||||||
|
|
||||||
|
# Laravel Broadcasting & Echo
|
||||||
|
|
||||||
|
## When to Apply
|
||||||
|
|
||||||
|
Activate this skill when:
|
||||||
|
|
||||||
|
- Installing or configuring Laravel broadcasting (Reverb, Pusher, Ably)
|
||||||
|
- Creating events that implement `ShouldBroadcast`
|
||||||
|
- Defining broadcast channels and authorization
|
||||||
|
- Setting up Laravel Echo on the client side
|
||||||
|
- Listening for broadcast events, notifications, or model events
|
||||||
|
- Implementing client-to-client events (whisper)
|
||||||
|
- Working with presence channels for user awareness
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Use `search-docs` for detailed broadcasting patterns and documentation.
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
### Installing Broadcasting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan install:broadcasting
|
||||||
|
```
|
||||||
|
|
||||||
|
Use flags for specific drivers: `--reverb`, `--pusher`, `--ably`. This creates `config/broadcasting.php` and `routes/channels.php`.
|
||||||
|
|
||||||
|
### Creating a Broadcast Event
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan make:event OrderShipped
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- Broadcast Event -->
|
||||||
|
```php
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class OrderShipped implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Order $order) {}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [new PrivateChannel('orders.'.$this->order->id)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Dispatch the event:
|
||||||
|
|
||||||
|
<!-- Dispatch Event -->
|
||||||
|
```php
|
||||||
|
use App\Events\OrderShipped;
|
||||||
|
|
||||||
|
OrderShipped::dispatch($order);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authorizing Channels
|
||||||
|
|
||||||
|
Define authorization in `routes/channels.php`:
|
||||||
|
|
||||||
|
<!-- Channel Authorization -->
|
||||||
|
```php
|
||||||
|
use App\Models\Order;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
|
||||||
|
return $user->id === Order::findOrNew($orderId)->user_id;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a channel class for complex authorization:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan make:channel OrderChannel
|
||||||
|
```
|
||||||
|
|
||||||
|
List all registered channels:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan channel:list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client-Side Setup
|
||||||
|
|
||||||
|
Install Echo and Pusher JS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-dev laravel-echo pusher-js
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- Echo Client Configuration -->
|
||||||
|
```javascript
|
||||||
|
import Echo from 'laravel-echo';
|
||||||
|
import Pusher from 'pusher-js';
|
||||||
|
window.Pusher = Pusher;
|
||||||
|
|
||||||
|
window.Echo = new Echo({
|
||||||
|
broadcaster: 'reverb',
|
||||||
|
key: import.meta.env.VITE_REVERB_APP_KEY,
|
||||||
|
wsHost: import.meta.env.VITE_REVERB_HOST,
|
||||||
|
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
|
||||||
|
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
|
||||||
|
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
|
||||||
|
enabledTransports: ['ws', 'wss'],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listening for Events
|
||||||
|
|
||||||
|
<!-- Listen on Private Channel -->
|
||||||
|
```javascript
|
||||||
|
Echo.private(`orders.${orderId}`)
|
||||||
|
.listen('OrderShipmentStatusUpdated', (e) => {
|
||||||
|
console.log(e.order);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Required Processes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan queue:work # Required for ShouldBroadcast events
|
||||||
|
|
||||||
|
php artisan reverb:start # Required for Reverb driver
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's Possible
|
||||||
|
|
||||||
|
Use `search-docs` to find detailed code examples and configuration for each of these:
|
||||||
|
|
||||||
|
### Channel Types
|
||||||
|
|
||||||
|
- Public (`new Channel`) — no auth, anyone can subscribe. Use for app-wide announcements, public feeds, or status pages.
|
||||||
|
- Private (`new PrivateChannel`) — requires authorization. Use for user-specific data like orders, messages, or account updates.
|
||||||
|
- Presence (`new PresenceChannel`) — authorized + tracks who's online. Use for chat rooms, collaborative editing, "who's viewing this" features, or typing indicators.
|
||||||
|
- EncryptedPrivate — end-to-end encryption, Pusher/Reverb only. Use when payload must be hidden from the broadcast server (e.g., sensitive financial data or private messages).
|
||||||
|
- Drivers: `reverb` (self-hosted WebSocket server), `pusher` (managed service), `ably` (managed service), `log` (writes to Laravel log, use for debugging), `null` (no-op, use for testing)
|
||||||
|
|
||||||
|
### Event Customization
|
||||||
|
|
||||||
|
- `broadcastAs()` — custom event name (client must use dot prefix: `.listen('.custom.name')`). Use when you want stable API names decoupled from PHP class names, or shorter event names for the frontend.
|
||||||
|
- `broadcastWith()` — control exact payload. Use to avoid leaking sensitive model attributes, slim down large payloads, or add computed data not on the model.
|
||||||
|
- `broadcastWhen()` — conditional broadcasting. Use to skip broadcasting when changes are trivial (e.g., only broadcast order updates above a threshold, or skip unchanged fields).
|
||||||
|
- `broadcastQueue()` / `$queue` — route to specific queue. Use to isolate real-time broadcasts from slow background jobs so they're processed faster.
|
||||||
|
- `$connection` — set queue connection per event. Use when broadcasts should go through a faster queue backend like Redis while other jobs use the database driver.
|
||||||
|
|
||||||
|
### Broadcasting Interfaces
|
||||||
|
|
||||||
|
- `ShouldBroadcast` — queue the broadcast (default). Use for most events to avoid blocking the HTTP response.
|
||||||
|
- `ShouldBroadcastNow` — broadcast synchronously, skip queue. Use during development or for time-critical events where queue latency is unacceptable.
|
||||||
|
- `ShouldDispatchAfterCommit` — wait for DB transaction commit. Use when the event references newly created records that listeners need to query (prevents race conditions).
|
||||||
|
- `ShouldRescue` — auto-catch broadcast exceptions. Use to prevent broadcast failures (e.g., WebSocket server down) from disrupting the user's HTTP request.
|
||||||
|
- `InteractsWithSockets` — required for `toOthers()`. Use on any event where you want to exclude the sender (optimistic UI updates).
|
||||||
|
- `InteractsWithBroadcasting` — override driver per event via `broadcastVia()`. Use in multi-driver setups (e.g., some events via Reverb, others via Pusher).
|
||||||
|
|
||||||
|
### Broadcasting Helpers
|
||||||
|
|
||||||
|
- `broadcast(new Event)->toOthers()` — exclude current user's socket. Use when the client already updates optimistically from the API response to avoid duplicate updates.
|
||||||
|
- `broadcast(new Event)->via('pusher')` — override connection. Use to route specific events through a different broadcast driver than the default.
|
||||||
|
- `Broadcast::on()`, `Broadcast::private()`, `Broadcast::presence()` — anonymous broadcasting without event classes. Chain `.as('name')->with($data)->send()` or `.sendNow()`. Use for simple one-off broadcasts where creating a full event class is overkill (e.g., quick status updates, simple notifications).
|
||||||
|
|
||||||
|
### Channel Authorization
|
||||||
|
|
||||||
|
- Closure-based in `routes/channels.php` — use for simple authorization logic (e.g., checking ownership).
|
||||||
|
- Model binding: `Broadcast::channel('orders.{order}', fn (User $user, Order $order) => ...)` — use when authorization depends on the model instance (auto-resolves from route parameter).
|
||||||
|
- Channel classes via `php artisan make:channel` — use for complex authorization logic that benefits from dependency injection or reusable logic across channels.
|
||||||
|
- Multiple guards: `['guards' => ['web', 'admin']]` — use when the channel should be accessible by users authenticated via different guards (e.g., both regular users and admins).
|
||||||
|
|
||||||
|
### Model Broadcasting
|
||||||
|
|
||||||
|
- `BroadcastsEvents` trait auto-broadcasts created/updated/deleted/trashed/restored. Use to automatically keep clients in sync with Eloquent model changes without writing individual events.
|
||||||
|
- Channel convention: `App.Models.Post.{id}` — clients subscribe to model-specific channels.
|
||||||
|
- `broadcastAs($event)` and `broadcastWith($event)` for per-action customization. Use to send different payloads for create vs update, or suppress certain event types.
|
||||||
|
- `newBroadcastableEvent($event)` for event instance customization (e.g., `->dontBroadcastToCurrentUser()`). Use when you need to modify the underlying event object before it's dispatched.
|
||||||
|
|
||||||
|
### Client-Side Features
|
||||||
|
|
||||||
|
- Client events: `whisper()` / `listenForWhisper()` — peer-to-peer without server roundtrip (private/presence channels only). Use for typing indicators, cursor positions, or any ephemeral state that doesn't need server persistence.
|
||||||
|
- Presence channels: `Echo.join()` with `here()`, `joining()`, `leaving()`, `error()` callbacks. Use for showing online users, "X is viewing this document" features, or live participant counts.
|
||||||
|
- Notification broadcasting: `.notification()` on user's private channel. Use to show real-time notifications (toast, badge counts) pushed from Laravel's notification system.
|
||||||
|
- Connection management: `Echo.connectionStatus()`, `Echo.leaveAllChannels()`, `Echo.disconnect()`. Use to show connection indicators, clean up on logout, or handle offline/reconnect scenarios.
|
||||||
|
- Custom namespace: `new Echo({ namespace: 'App.Other.Namespace' })`. Use when your events live outside the default `App\Events` namespace.
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- Queue worker must be running for `ShouldBroadcast` events. Use `ShouldBroadcastNow` during development.
|
||||||
|
- `BROADCAST_CONNECTION` not `BROADCAST_DRIVER`: Laravel 11+ renamed this env key.
|
||||||
|
- `toOthers()` requires `InteractsWithSockets` trait AND `X-Socket-ID` header. Echo auto-adds this to global Axios. For `fetch`, manually send `Echo.socketId()`.
|
||||||
|
- CORS: When frontend/backend are on different origins, add `broadcasting/auth` to `config/cors.php` paths and set `supports_credentials` to `true`.
|
||||||
|
- Missing `VITE_` prefix: Client-side env vars must start with `VITE_`.
|
||||||
|
- `channels.php` not loaded: Verify it's included in `withRouting()` in `bootstrap/app.php`.
|
||||||
|
- Reverb is long-running: Code changes require `php artisan reverb:restart`.
|
||||||
|
- Presence channel auth must return an array of user data (`['id' => $user->id, 'name' => $user->name]`), not `true`. Returning `true` silently fails.
|
||||||
|
- Dot prefix rule: When using `broadcastAs()`, client must prefix with `.` (e.g., `.listen('.custom.name')`). Without the dot, Echo looks for `App\Events\custom.name` which silently fails.
|
||||||
|
- Reverb host separation: `REVERB_SERVER_HOST`/`REVERB_SERVER_PORT` (internal bind) vs `REVERB_HOST`/`REVERB_PORT` (public address) vs `VITE_REVERB_HOST`/`VITE_REVERB_PORT` (client JS).
|
||||||
|
- Sanctum SPA auth: Ensure `/broadcasting/auth` uses `auth:sanctum` middleware and CSRF tokens are sent with `withCredentials: true`.
|
||||||
406
.junie/skills/echo-vue-development/SKILL.md
Normal file
406
.junie/skills/echo-vue-development/SKILL.md
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
---
|
||||||
|
name: echo-vue-development
|
||||||
|
description: "Develops real-time broadcasting in Vue applications with Laravel Echo. Activates when configuring Echo in Vue (configureEcho); using composables (useEcho, useEchoPublic, useEchoPresence, useEchoModel, useEchoNotification, useConnectionStatus); listening for broadcast events in Vue components; implementing client events (whisper) in Vue; or when the user mentions Echo with Vue, real-time Vue composables, or broadcasting in Vue components."
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
author: laravel
|
||||||
|
---
|
||||||
|
|
||||||
|
# Laravel Echo Vue Integration
|
||||||
|
|
||||||
|
## When to Apply
|
||||||
|
|
||||||
|
Activate this skill when:
|
||||||
|
|
||||||
|
- Configuring Echo in a Vue application (`configureEcho`)
|
||||||
|
- Using Echo composables in Vue components
|
||||||
|
- Listening for broadcast events, model events, or notifications in Vue
|
||||||
|
- Implementing client events (whisper) in Vue
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Use `search-docs` for detailed broadcasting patterns. Search for:
|
||||||
|
|
||||||
|
- "receiving broadcasts" — composable usage with full examples
|
||||||
|
- "model broadcasting" — useEchoModel for Eloquent model events
|
||||||
|
- "client events" — whisper/listenForWhisper
|
||||||
|
- "presence channels" — useEchoPresence with member tracking
|
||||||
|
- "broadcasting installation" — configureEcho setup
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
### Configure Echo
|
||||||
|
|
||||||
|
Call once in your app entry point (e.g., `app.ts`):
|
||||||
|
|
||||||
|
<!-- Configure Echo for Reverb -->
|
||||||
|
```typescript
|
||||||
|
import { configureEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
configureEcho({
|
||||||
|
broadcaster: "reverb",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
All Reverb connection options (`key`, `wsHost`, `wsPort`, `wssPort`, `forceTLS`, `enabledTransports`) are auto-read from environment variables when omitted. Override explicitly only when needed.
|
||||||
|
|
||||||
|
For Pusher:
|
||||||
|
|
||||||
|
<!-- Configure Echo for Pusher -->
|
||||||
|
```typescript
|
||||||
|
import { configureEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
configureEcho({
|
||||||
|
broadcaster: "pusher",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listen for Events
|
||||||
|
|
||||||
|
<!-- Private Channel Composable -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
const props = defineProps<{ orderId: number }>();
|
||||||
|
|
||||||
|
useEcho(`orders.${props.orderId}`, "OrderShipmentStatusUpdated", (e) => {
|
||||||
|
console.log(e.order);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
`useEcho` defaults to private channels, subscribes on mount, unsubscribes on unmount.
|
||||||
|
|
||||||
|
Listen to multiple events:
|
||||||
|
|
||||||
|
<!-- Multiple Events -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
useEcho(
|
||||||
|
`orders.${orderId}`,
|
||||||
|
["OrderShipmentStatusUpdated", "OrderShipped"],
|
||||||
|
(e) => {
|
||||||
|
console.log(e.order);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Public Channels
|
||||||
|
|
||||||
|
<!-- Public Channel Composable -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEchoPublic } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
useEchoPublic("posts", "PostPublished", (e) => {
|
||||||
|
console.log(e.post);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Presence Channels
|
||||||
|
|
||||||
|
<!-- Presence Channel Composable -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEchoPresence } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
const { channel } = useEchoPresence("chat.1", "NewMessage", (e) => {
|
||||||
|
console.log(e.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
channel().here((users) => console.log('Current users:', users));
|
||||||
|
channel().joining((user) => console.log(`${user.name} joined`));
|
||||||
|
channel().leaving((user) => console.log(`${user.name} left`));
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Model Broadcasting
|
||||||
|
|
||||||
|
<!-- Model Broadcasting Composable -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEchoModel } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
const props = defineProps<{ userId: number }>();
|
||||||
|
|
||||||
|
useEchoModel("App.Models.User", props.userId, ["UserUpdated"], (e) => {
|
||||||
|
console.log(e.model);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notifications
|
||||||
|
|
||||||
|
<!-- Notification Composable -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEchoNotification } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
useEchoNotification(`App.Models.User.${userId}`, (notification) => {
|
||||||
|
console.log(notification);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Events (Whisper)
|
||||||
|
|
||||||
|
<!-- Client Events in Vue -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
const { channel } = useEcho(`chat.${roomId}`, ['update'], (e) => {
|
||||||
|
console.log('Chat event received:', e);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send typing indicator
|
||||||
|
channel().whisper('typing', { name: user.name });
|
||||||
|
|
||||||
|
// Listen for typing
|
||||||
|
channel().listenForWhisper('typing', (e) => {
|
||||||
|
console.log(`${e.name} is typing...`);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Status
|
||||||
|
|
||||||
|
<!-- Connection Status -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useConnectionStatus } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
const status = useConnectionStatus();
|
||||||
|
// Possible values: connected, connecting, reconnecting, disconnected, failed
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>Connection: {{ status }}</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Safety
|
||||||
|
|
||||||
|
Specify payload shape using TypeScript generics:
|
||||||
|
|
||||||
|
<!-- Type-safe Event Listening -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
type OrderData = {
|
||||||
|
order: { id: number; user: { id: number; name: string } };
|
||||||
|
};
|
||||||
|
|
||||||
|
useEcho<OrderData>(`orders.${orderId}`, "OrderShipmentStatusUpdated", (e) => {
|
||||||
|
console.log(e.order.id);
|
||||||
|
console.log(e.order.user.name);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
For model broadcasts:
|
||||||
|
|
||||||
|
<!-- Type-safe Model Broadcasting -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEchoModel } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
type User = { id: number; name: string; email: string };
|
||||||
|
|
||||||
|
useEchoModel<User, "App.Models.User">("App.Models.User", userId, ["UserUpdated"], (e) => {
|
||||||
|
console.log(e.model.name);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Control
|
||||||
|
|
||||||
|
All composables return methods for manual control:
|
||||||
|
|
||||||
|
<!-- Manual Control -->
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useEcho } from "@laravel/echo-vue";
|
||||||
|
|
||||||
|
const { leaveChannel, leave, stopListening, listen } = useEcho(
|
||||||
|
`orders.${orderId}`,
|
||||||
|
"OrderShipmentStatusUpdated",
|
||||||
|
(e) => {
|
||||||
|
console.log(e.order);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stop listening without leaving channel...
|
||||||
|
stopListening();
|
||||||
|
|
||||||
|
// Start listening again...
|
||||||
|
listen();
|
||||||
|
|
||||||
|
// Leave channel...
|
||||||
|
leaveChannel();
|
||||||
|
|
||||||
|
// Leave a channel and its associated private and presence channels...
|
||||||
|
leave();
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Composables
|
||||||
|
|
||||||
|
- `useEcho(channel, event, callback, deps?, visibility?)` — Private channels (default). Supports single or array of events.
|
||||||
|
- `useEchoPublic(channel, event, callback, deps?)` — Public channels (no auth)
|
||||||
|
- `useEchoPresence(channel, event, callback, deps?)` — Presence channels with member tracking
|
||||||
|
- `useEchoModel(model, id, events, callback, deps?)` — Eloquent model events (auto-constructs channel name, auto-adds dot prefix)
|
||||||
|
- `useEchoNotification(channel, callback, event?, deps?)` — Broadcast notifications
|
||||||
|
- `useConnectionStatus()` — Returns a `Ref<ConnectionStatus>` that updates reactively
|
||||||
|
|
||||||
|
All composables return `{ listen, stopListening, leaveChannel, leave, channel }` for manual control.
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
|
||||||
|
- `configureEcho(options)` — Configure the singleton Echo instance (call once in app entry point)
|
||||||
|
- `echo()` — Access the Echo instance directly (e.g., `echo().socketId()` for the X-Socket-ID header)
|
||||||
|
- `echoIsConfigured()` — Check if Echo has been configured before accessing `echo()`
|
||||||
|
|
||||||
|
### Additional Capabilities via channel()
|
||||||
|
|
||||||
|
- Client events: `channel().whisper("typing", data)` / `channel().listenForWhisper("typing", cb)`
|
||||||
|
- Notifications: `channel().notification(cb)` — alternative to `useEchoNotification`
|
||||||
|
- Channel lifecycle: `channel().subscribed(cb)` / `channel().error(cb)`
|
||||||
|
|
||||||
|
## Server-Side Reference
|
||||||
|
|
||||||
|
Use `search-docs` for detailed code examples. This section covers what's available on the backend so you can build the full end-to-end flow.
|
||||||
|
|
||||||
|
### Creating Broadcast Events
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan make:event OrderShipped
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- Broadcast Event -->
|
||||||
|
```php
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class OrderShipped implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Order $order) {}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [new PrivateChannel('orders.'.$this->order->id)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Channel Authorization
|
||||||
|
|
||||||
|
Define in `routes/channels.php`:
|
||||||
|
|
||||||
|
<!-- Channel Authorization -->
|
||||||
|
```php
|
||||||
|
use App\Models\Order;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
|
||||||
|
return $user->id === Order::findOrNew($orderId)->user_id;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a channel class for complex authorization:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan make:channel OrderChannel
|
||||||
|
```
|
||||||
|
|
||||||
|
List all registered channels:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan channel:list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Channel Types
|
||||||
|
|
||||||
|
- Public (`new Channel`) — no auth, anyone can subscribe. Use for app-wide announcements, public feeds, or status pages.
|
||||||
|
- Private (`new PrivateChannel`) — requires authorization. Use for user-specific data like orders, messages, or account updates.
|
||||||
|
- Presence (`new PresenceChannel`) — authorized + tracks who's online. Use for chat rooms, collaborative editing, "who's viewing this" features, or typing indicators.
|
||||||
|
- EncryptedPrivate — end-to-end encryption, Pusher/Reverb only. Use when payload must be hidden from the broadcast server (e.g., sensitive financial data or private messages).
|
||||||
|
- Drivers: `reverb` (self-hosted WebSocket server), `pusher` (managed service), `ably` (managed service), `log` (writes to Laravel log, use for debugging), `null` (no-op, use for testing)
|
||||||
|
|
||||||
|
### Event Customization
|
||||||
|
|
||||||
|
- `broadcastAs()` — custom event name (client must use dot prefix: `.listen('.custom.name')`). Use when you want stable API names decoupled from PHP class names, or shorter event names for the frontend.
|
||||||
|
- `broadcastWith()` — control exact payload. Use to avoid leaking sensitive model attributes, slim down large payloads, or add computed data not on the model.
|
||||||
|
- `broadcastWhen()` — conditional broadcasting. Use to skip broadcasting when changes are trivial (e.g., only broadcast order updates above a threshold, or skip unchanged fields).
|
||||||
|
- `broadcastQueue()` / `$queue` — route to specific queue. Use to isolate real-time broadcasts from slow background jobs so they're processed faster.
|
||||||
|
- `$connection` — set queue connection per event. Use when broadcasts should go through a faster queue backend like Redis while other jobs use the database driver.
|
||||||
|
|
||||||
|
### Broadcasting Interfaces
|
||||||
|
|
||||||
|
- `ShouldBroadcast` — queue the broadcast (default). Use for most events to avoid blocking the HTTP response.
|
||||||
|
- `ShouldBroadcastNow` — broadcast synchronously, skip queue. Use during development or for time-critical events where queue latency is unacceptable.
|
||||||
|
- `ShouldDispatchAfterCommit` — wait for DB transaction commit. Use when the event references newly created records that listeners need to query (prevents race conditions).
|
||||||
|
- `ShouldRescue` — auto-catch broadcast exceptions. Use to prevent broadcast failures (e.g., WebSocket server down) from disrupting the user's HTTP request.
|
||||||
|
- `InteractsWithSockets` — required for `toOthers()`. Use on any event where you want to exclude the sender (optimistic UI updates).
|
||||||
|
- `InteractsWithBroadcasting` — override driver per event via `broadcastVia()`. Use in multi-driver setups (e.g., some events via Reverb, others via Pusher).
|
||||||
|
|
||||||
|
### Broadcasting Helpers
|
||||||
|
|
||||||
|
- `broadcast(new Event)->toOthers()` — exclude current user's socket. Use when the client already updates optimistically from the API response to avoid duplicate updates.
|
||||||
|
- `broadcast(new Event)->via('pusher')` — override connection. Use to route specific events through a different broadcast driver than the default.
|
||||||
|
- `Broadcast::on()`, `Broadcast::private()`, `Broadcast::presence()` — anonymous broadcasting without event classes. Chain `.as('name')->with($data)->send()` or `.sendNow()`. Use for simple one-off broadcasts where creating a full event class is overkill (e.g., quick status updates, simple notifications).
|
||||||
|
|
||||||
|
### Channel Authorization Options
|
||||||
|
|
||||||
|
- Closure-based in `routes/channels.php` — use for simple authorization logic (e.g., checking ownership).
|
||||||
|
- Model binding: `Broadcast::channel('orders.{order}', fn (User $user, Order $order) => ...)` — use when authorization depends on the model instance (auto-resolves from route parameter).
|
||||||
|
- Channel classes via `php artisan make:channel` — use for complex authorization logic that benefits from dependency injection or reusable logic across channels.
|
||||||
|
- Multiple guards: `['guards' => ['web', 'admin']]` — use when the channel should be accessible by users authenticated via different guards (e.g., both regular users and admins).
|
||||||
|
|
||||||
|
### Model Broadcasting (Server-Side)
|
||||||
|
|
||||||
|
- `BroadcastsEvents` trait auto-broadcasts created/updated/deleted/trashed/restored. Use to automatically keep clients in sync with Eloquent model changes without writing individual events.
|
||||||
|
- Channel convention: `App.Models.Post.{id}` — matches `useEchoModel` first argument.
|
||||||
|
- `broadcastAs($event)` and `broadcastWith($event)` for per-action customization. Use to send different payloads for create vs update, or suppress certain event types.
|
||||||
|
- `newBroadcastableEvent($event)` for event instance customization (e.g., `->dontBroadcastToCurrentUser()`). Use when you need to modify the underlying event object before it's dispatched.
|
||||||
|
|
||||||
|
### Running Required Processes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan queue:work # Required for ShouldBroadcast events
|
||||||
|
|
||||||
|
php artisan reverb:start # Required for Reverb driver
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- Queue worker must be running for `ShouldBroadcast` events. Use `ShouldBroadcastNow` during development.
|
||||||
|
- `BROADCAST_CONNECTION` not `BROADCAST_DRIVER`: Laravel 11+ renamed this env key.
|
||||||
|
- Presence channel auth must return an array of user data (`['id' => $user->id, 'name' => $user->name]`), not `true`. Returning `true` silently fails.
|
||||||
|
- Dot prefix rule: When using `broadcastAs()`, client must prefix with `.` (e.g., `.listen('.custom.name')`). Without the dot, Echo looks for `App\Events\custom.name` which silently fails.
|
||||||
|
- CORS: When frontend/backend are on different origins, add `broadcasting/auth` to `config/cors.php` paths and set `supports_credentials` to `true`.
|
||||||
|
- `channels.php` not loaded: Verify it's included in `withRouting()` in `bootstrap/app.php`.
|
||||||
|
- Reverb is long-running: Code changes require `php artisan reverb:restart`.
|
||||||
|
- Call `configureEcho` before any composables run. Place it in your app entry point (e.g., `app.ts`), not inside a component's `<script setup>`.
|
||||||
|
- Composables auto-cleanup on unmount — do NOT manually call `leave()` or `stopListening()` in `onUnmounted`.
|
||||||
|
- Composables must be called in `<script setup>` or `setup()` — they rely on the Vue lifecycle.
|
||||||
|
- `X-Socket-ID` header is NOT auto-sent with Inertia requests. Manually add `echo().socketId()` when using `broadcast()->toOthers()`.
|
||||||
|
- SSR / "window is not defined": Guard `configureEcho` with `typeof window !== 'undefined'` in Nuxt/SSR contexts.
|
||||||
|
- One Echo instance: `configureEcho` creates a singleton. Multiple calls reuse the first configuration.
|
||||||
|
- Channel reference counting: Multiple components sharing a channel name share one subscription. The channel is only left when ALL components unmount. Don't call `leave()` unless you want to force-unsubscribe all listeners.
|
||||||
|
- Dependencies array: Pass reactive state (from `ref()` or props) in the deps array so composables re-subscribe with fresh callbacks.
|
||||||
|
- Custom event names need dot prefix: When the server uses `broadcastAs()`, listen with the exact custom name. But `useEchoModel` automatically adds the dot prefix.
|
||||||
@ -16,6 +16,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||||||
- laravel/prompts (PROMPTS) - v0
|
- laravel/prompts (PROMPTS) - v0
|
||||||
- laravel/reverb (REVERB) - v1
|
- laravel/reverb (REVERB) - v1
|
||||||
- laravel/wayfinder (WAYFINDER) - v0
|
- laravel/wayfinder (WAYFINDER) - v0
|
||||||
|
- tightenco/ziggy (ZIGGY) - v2
|
||||||
- larastan/larastan (LARASTAN) - v3
|
- larastan/larastan (LARASTAN) - v3
|
||||||
- laravel/boost (BOOST) - v2
|
- laravel/boost (BOOST) - v2
|
||||||
- laravel/mcp (MCP) - v0
|
- laravel/mcp (MCP) - v0
|
||||||
@ -27,8 +28,10 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||||||
- @inertiajs/vue3 (INERTIA_VUE) - v3
|
- @inertiajs/vue3 (INERTIA_VUE) - v3
|
||||||
- tailwindcss (TAILWINDCSS) - v4
|
- tailwindcss (TAILWINDCSS) - v4
|
||||||
- vue (VUE) - v3
|
- vue (VUE) - v3
|
||||||
|
- @laravel/echo-vue (ECHO_VUE) - v2
|
||||||
- @laravel/vite-plugin-wayfinder (WAYFINDER_VITE) - v0
|
- @laravel/vite-plugin-wayfinder (WAYFINDER_VITE) - v0
|
||||||
- eslint (ESLINT) - v9
|
- eslint (ESLINT) - v9
|
||||||
|
- laravel-echo (ECHO) - v2
|
||||||
- prettier (PRETTIER) - v3
|
- prettier (PRETTIER) - v3
|
||||||
|
|
||||||
## Skills Activation
|
## Skills Activation
|
||||||
|
|||||||
@ -4,11 +4,13 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Http\Requests\StoreDynamicRequest;
|
use App\Http\Requests\StoreDynamicRequest;
|
||||||
use App\Models\Dynamic;
|
use App\Models\Dynamic;
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class DynamicController extends Controller
|
class DynamicController extends Controller
|
||||||
{
|
{
|
||||||
|
use AuthorizesRequests;
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -13,6 +13,8 @@
|
|||||||
"wayfinder-development",
|
"wayfinder-development",
|
||||||
"pest-testing",
|
"pest-testing",
|
||||||
"inertia-vue-development",
|
"inertia-vue-development",
|
||||||
"tailwindcss-development"
|
"tailwindcss-development",
|
||||||
|
"echo-vue-development",
|
||||||
|
"echo-development"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,4 +8,6 @@ return [
|
|||||||
AppServiceProvider::class,
|
AppServiceProvider::class,
|
||||||
AuthServiceProvider::class,
|
AuthServiceProvider::class,
|
||||||
FortifyServiceProvider::class,
|
FortifyServiceProvider::class,
|
||||||
|
\Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||||
|
\Tighten\Ziggy\ZiggyServiceProvider::class,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -9,14 +9,15 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3",
|
"php": "^8.4",
|
||||||
"inertiajs/inertia-laravel": "^3.0",
|
"inertiajs/inertia-laravel": "^3.0",
|
||||||
"laravel/chisel": "^0.1.0",
|
"laravel/chisel": "^0.1.0",
|
||||||
"laravel/fortify": "^1.37.2",
|
"laravel/fortify": "^1.37.2",
|
||||||
"laravel/framework": "^13.7",
|
"laravel/framework": "^13.7",
|
||||||
"laravel/reverb": "^1.10",
|
"laravel/reverb": "^1.10",
|
||||||
"laravel/tinker": "^3.0",
|
"laravel/tinker": "^3.0",
|
||||||
"laravel/wayfinder": "^0.1.14"
|
"laravel/wayfinder": "^0.1.14",
|
||||||
|
"tightenco/ziggy": "^2.6"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.24",
|
"fakerphp/faker": "^1.24",
|
||||||
|
|||||||
76
composer.lock
generated
76
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "eb506fd975e79f12430ecb1c6cd824ac",
|
"content-hash": "4f6fe33dc680e6446bd6318d5bdd9ec9",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@ -8035,6 +8035,76 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-05-29T05:06:50+00:00"
|
"time": "2026-05-29T05:06:50+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "tightenco/ziggy",
|
||||||
|
"version": "v2.6.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tighten/ziggy.git",
|
||||||
|
"reference": "8a0b645921623f77dceaf543d61ecd51a391d96e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/tighten/ziggy/zipball/8a0b645921623f77dceaf543d61ecd51a391d96e",
|
||||||
|
"reference": "8a0b645921623f77dceaf543d61ecd51a391d96e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"laravel/framework": ">=9.0",
|
||||||
|
"php": ">=8.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/folio": "^1.1",
|
||||||
|
"orchestra/testbench": "^8.0 || ^9.0 || ^10.0",
|
||||||
|
"pestphp/pest": "^2.0 || ^3.0 || ^4.0",
|
||||||
|
"pestphp/pest-plugin-laravel": "^2.0 || ^3.0 || ^4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Tighten\\Ziggy\\ZiggyServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tighten\\Ziggy\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Daniel Coulbourne",
|
||||||
|
"email": "daniel@tighten.co"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jake Bathman",
|
||||||
|
"email": "jake@tighten.co"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jacob Baker-Kretzmar",
|
||||||
|
"email": "jacob@tighten.co"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Use your Laravel named routes in JavaScript.",
|
||||||
|
"homepage": "https://github.com/tighten/ziggy",
|
||||||
|
"keywords": [
|
||||||
|
"Ziggy",
|
||||||
|
"javascript",
|
||||||
|
"laravel",
|
||||||
|
"routes"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/tighten/ziggy/issues",
|
||||||
|
"source": "https://github.com/tighten/ziggy/tree/v2.6.2"
|
||||||
|
},
|
||||||
|
"time": "2026-03-05T14:41:19+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tijsverkoyen/css-to-inline-styles",
|
"name": "tijsverkoyen/css-to-inline-styles",
|
||||||
"version": "v2.4.0",
|
"version": "v2.4.0",
|
||||||
@ -12178,8 +12248,8 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.3"
|
"php": "^8.4"
|
||||||
},
|
},
|
||||||
"platform-dev": {},
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.9.0"
|
"plugin-api-version": "2.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,9 @@ import { configureEcho } from '@laravel/echo-vue';
|
|||||||
|
|
||||||
configureEcho({
|
configureEcho({
|
||||||
broadcaster: 'reverb',
|
broadcaster: 'reverb',
|
||||||
key: import.meta.env.VITE_REVERB_APP_KEY,
|
|
||||||
host: import.meta.env.VITE_REVERB_HOST,
|
|
||||||
port: import.meta.env.VITE_REVERB_PORT,
|
|
||||||
scheme: import.meta.env.VITE_REVERB_SCHEME,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
||||||
|
|
||||||
createInertiaApp({
|
createInertiaApp({
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from '@/layouts/AppLayout.vue';
|
|
||||||
import { Head, useForm } from '@inertiajs/vue3';
|
import { Head, useForm } from '@inertiajs/vue3';
|
||||||
import { route } from 'ziggy-js';
|
import { route } from 'ziggy-js';
|
||||||
|
|
||||||
@ -27,7 +26,6 @@ function submit() {
|
|||||||
<template>
|
<template>
|
||||||
<Head title="Create Dynamic" />
|
<Head title="Create Dynamic" />
|
||||||
|
|
||||||
<AppLayout :breadcrumbs="breadcrumbs">
|
|
||||||
<div class="py-12">
|
<div class="py-12">
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
@ -57,5 +55,4 @@ function submit() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from '@/layouts/AppLayout.vue';
|
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
import { route } from 'ziggy-js';
|
import { route } from 'ziggy-js';
|
||||||
|
|
||||||
@ -18,7 +17,6 @@ const breadcrumbs = [
|
|||||||
<template>
|
<template>
|
||||||
<Head title="Dynamics" />
|
<Head title="Dynamics" />
|
||||||
|
|
||||||
<AppLayout :breadcrumbs="breadcrumbs">
|
|
||||||
<div class="py-12">
|
<div class="py-12">
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
@ -45,5 +43,4 @@ const breadcrumbs = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from '@/layouts/AppLayout.vue';
|
|
||||||
import Chat from '@/components/Chat.vue';
|
import Chat from '@/components/Chat.vue';
|
||||||
import { Head, Link, useForm } from '@inertiajs/vue3';
|
import { Head, Link, useForm } from '@inertiajs/vue3';
|
||||||
import { route } from 'ziggy-js';
|
import { route } from 'ziggy-js';
|
||||||
@ -34,7 +33,6 @@ function submit() {
|
|||||||
<template>
|
<template>
|
||||||
<Head :title="dynamic.name" />
|
<Head :title="dynamic.name" />
|
||||||
|
|
||||||
<AppLayout :breadcrumbs="breadcrumbs">
|
|
||||||
<div class="py-12">
|
<div class="py-12">
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
@ -101,5 +99,4 @@ function submit() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AppLayout from '@/layouts/AppLayout.vue';
|
|
||||||
import Chat from '@/components/Chat.vue';
|
import Chat from '@/components/Chat.vue';
|
||||||
import { Head, useForm } from '@inertiajs/vue3';
|
import { Head, useForm } from '@inertiajs/vue3';
|
||||||
import { route } from 'ziggy-js';
|
import { route } from 'ziggy-js';
|
||||||
@ -39,7 +38,6 @@ function submit() {
|
|||||||
<template>
|
<template>
|
||||||
<Head :title="ledger.name" />
|
<Head :title="ledger.name" />
|
||||||
|
|
||||||
<AppLayout :breadcrumbs="breadcrumbs">
|
|
||||||
<div class="py-12">
|
<div class="py-12">
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
@ -95,5 +93,4 @@ function submit() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
|
|
||||||
@fonts
|
@fonts
|
||||||
|
@routes
|
||||||
|
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.ts', "resources/js/pages/{$page['component']}.vue"])
|
@vite(['resources/css/app.css', 'resources/js/app.ts', "resources/js/pages/{$page['component']}.vue"])
|
||||||
<x-inertia::head>
|
<x-inertia::head>
|
||||||
|
|||||||
@ -18,4 +18,5 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
Route::post('/chats/{chat}/messages', [MessageController::class, 'store'])->name('chats.messages.store');
|
Route::post('/chats/{chat}/messages', [MessageController::class, 'store'])->name('chats.messages.store');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Broadcast::routes();
|
||||||
require __DIR__.'/settings.php';
|
require __DIR__.'/settings.php';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user