2026-01-22 23:38:00 +01:00
< div class = " p-6 " >
2026-07-25 20:15:52 +02:00
< x - page - header title = " Channels " subtitle = " Manage your platform channels and linked accounts " >
< button
wire : click = " openCreateModal "
2026-08-10 01:46:02 +02:00
class = " inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md shadow-xs hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 "
2026-07-25 20:15:52 +02:00
>
< svg class = " h-4 w-4 mr-1.5 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 2 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round " d = " M12 4.5v15m7.5-7.5h-15 " />
</ svg >
Add Channel
</ button >
</ x - page - header >
2026-01-22 23:38:00 +01:00
< div class = " grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 " >
@ forelse ( $channels as $channel )
2026-08-10 19:40:22 +02:00
< div class = " bg-white rounded-lg shadow-sm p-6 dark:bg-gray-800 " wire : key = " channel- { { $channel->id }} " >
2026-01-22 23:38:00 +01:00
< div class = " flex items-start justify-between " >
< div class = " flex items-center " >
< div class = " shrink-0 " >
< svg class = " h-8 w-8 text-indigo-500 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 1.5 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round " d = " M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5 " />
</ svg >
</ div >
< div class = " ml-4 flex-1 min-w-0 " >
2026-08-10 19:40:22 +02:00
< h3 class = " text-lg font-medium text-gray-900 truncate dark:text-gray-100 " >
2026-01-22 23:38:00 +01:00
{{ $channel -> display_name ? ? $channel -> name }}
</ h3 >
@ if ( $channel -> platformInstance )
< a href = " { { $channel->platformInstance ->url }}/c/ { { $channel->name }} "
target = " _blank "
rel = " noopener noreferrer "
class = " text-sm text-blue-500 hover:underline " >
{{ $channel -> platformInstance -> name }}
</ a >
@ endif
</ div >
</ div >
2026-08-13 18:44:59 +02:00
< div class = " flex shrink-0 items-center space-x-1 " >
< button
wire : click = " openEditModal( { { $channel->id }}) "
class = " p-1 rounded-full text-gray-400 hover:bg-gray-100 hover:text-gray-600 dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-gray-300 "
title = " Edit channel "
aria - label = " Edit { { $channel->display_name }} "
>
< svg class = " h-5 w-5 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 1.5 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round " d = " m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125 " />
</ svg >
</ button >
< button
wire : click = " toggle( { { $channel->id }}) "
class = " p-1 rounded-full { { $channel->is_active ? 'bg-green-100 text-green-600 dark:bg-green-900/30 dark:text-green-400' : 'bg-gray-100 text-gray-400 dark:bg-gray-700 dark:text-gray-500' }} "
title = " { { $channel->is_active ? 'Active - Click to disable' : 'Inactive - Click to enable' }} "
>
< svg class = " h-5 w-5 " fill = " currentColor " viewBox = " 0 0 20 20 " >
< path fill - rule = " evenodd " d = " M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z " clip - rule = " evenodd " />
</ svg >
</ button >
2026-08-14 00:54:17 +02:00
@ php ( $impact = $deletionImpact [ $channel -> id ] ? ? [ 'articles' => 0 , 'publications' => 0 ])
< button
wire : click = " deleteChannel( { { $channel->id }}) "
wire : confirm = " Delete " { { $channel->display_name }}"? This also removes its routes, { { $impact['articles'] }} article { { $impact['articles'] === 1 ? '' : 's' }} routed to it and { { $impact['publications'] }} publication record { { $impact['publications'] === 1 ? '' : 's' }}. This cannot be undone. "
class = " p-1 rounded-full text-gray-400 hover:bg-red-50 hover:text-red-600 dark:text-gray-500 dark:hover:bg-red-900/20 dark:hover:text-red-400 "
title = " Delete channel "
aria - label = " Delete { { $channel->display_name }} "
>
< svg class = " h-5 w-5 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 1.5 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round " d = " m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0 " />
</ svg >
</ button >
2026-08-13 18:44:59 +02:00
</ div >
2026-01-22 23:38:00 +01:00
</ div >
@ if ( $channel -> description )
2026-08-10 19:40:22 +02:00
< p class = " mt-3 text-sm text-gray-500 line-clamp-2 dark:text-gray-400 " >
2026-01-22 23:38:00 +01:00
{{ $channel -> description }}
</ p >
@ endif
< div class = " mt-4 " >
< div class = " flex items-center justify-between mb-2 " >
2026-08-10 19:40:22 +02:00
< span class = " text-sm font-medium text-gray-700 dark:text-gray-200 " >
2026-01-22 23:38:00 +01:00
Linked Accounts ({{ $channel -> platformAccounts -> count () }})
</ span >
< button
wire : click = " openAccountModal( { { $channel->id }}) "
2026-08-10 19:40:22 +02:00
class = " text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 "
2026-01-22 23:38:00 +01:00
>
Manage
</ button >
</ div >
@ if ( $channel -> platformAccounts -> isNotEmpty ())
< div class = " space-y-1 " >
@ foreach ( $channel -> platformAccounts -> take ( 3 ) as $account )
< div class = " flex items-center justify-between text-sm " >
2026-08-11 00:44:26 +02:00
< span class = " flex items-center gap-x-1.5 " >
< span class = " text-gray-600 dark:text-gray-300 " > {{ $account -> username }} </ span >
@ if ( $account -> isUnhealthy ())
< span class = " inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300 " title = " Login failed on the last { { App \ Models \ PlatformAccount::FAILURES_BEFORE_UNHEALTHY }} checks " >
Credentials failing
</ span >
@ endif
</ span >
2026-01-22 23:38:00 +01:00
< button
wire : click = " detachAccount( { { $channel->id }}, { { $account->id }}) "
class = " text-red-500 hover:text-red-700 "
title = " Remove account "
>
< svg class = " h-4 w-4 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 1.5 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round " d = " M6 18 18 6M6 6l12 12 " />
</ svg >
</ button >
</ div >
@ endforeach
@ if ( $channel -> platformAccounts -> count () > 3 )
2026-08-10 19:40:22 +02:00
< span class = " text-xs text-gray-400 dark:text-gray-500 " >+ {{ $channel -> platformAccounts -> count () - 3 }} more </ span >
2026-01-22 23:38:00 +01:00
@ endif
</ div >
@ else
2026-08-10 19:40:22 +02:00
< p class = " text-sm text-gray-400 dark:text-gray-500 " > No accounts linked </ p >
2026-01-22 23:38:00 +01:00
@ endif
</ div >
< div class = " mt-4 flex items-center justify-between " >
2026-08-10 19:40:22 +02:00
< span class = " inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium { { $channel->is_active ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300' : 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200' }} " >
2026-01-22 23:38:00 +01:00
{{ $channel -> is_active ? 'Active' : 'Inactive' }}
</ span >
2026-08-10 19:40:22 +02:00
< span class = " text-xs text-gray-500 dark:text-gray-400 " >
2026-01-22 23:38:00 +01:00
{{ $channel -> created_at -> format ( 'M d, Y' ) }}
</ span >
</ div >
</ div >
@ empty
< div class = " col-span-full text-center py-12 " >
2026-08-10 19:40:22 +02:00
< svg class = " mx-auto h-12 w-12 text-gray-400 dark:text-gray-500 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 1.5 " stroke = " currentColor " >
2026-01-22 23:38:00 +01:00
< path stroke - linecap = " round " stroke - linejoin = " round " d = " M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5 " />
</ svg >
2026-08-10 19:40:22 +02:00
< h3 class = " mt-2 text-sm font-medium text-gray-900 dark:text-gray-100 " > No channels </ h3 >
< p class = " mt-1 text-sm text-gray-500 dark:text-gray-400 " >
2026-01-22 23:38:00 +01:00
No platform channels have been configured yet .
</ p >
2026-07-25 20:15:52 +02:00
< div class = " mt-6 " >
< button
wire : click = " openCreateModal "
2026-08-10 01:46:02 +02:00
class = " inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md shadow-xs hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 "
2026-07-25 20:15:52 +02:00
>
< svg class = " h-4 w-4 mr-1.5 " fill = " none " viewBox = " 0 0 24 24 " stroke - width = " 2 " stroke = " currentColor " >
< path stroke - linecap = " round " stroke - linejoin = " round " d = " M12 4.5v15m7.5-7.5h-15 " />
</ svg >
Add Channel
</ button >
</ div >
2026-01-22 23:38:00 +01:00
</ div >
@ endforelse
</ div >
<!-- Account Management Modal -->
@ if ( $managingChannel )
2026-08-01 00:11:06 +02:00
< x - form - modal : title = " 'Manage Accounts for ' . ( $managingChannel->display_name ?? $managingChannel->name ) " close = " closeAccountModal " >
2026-01-22 23:38:00 +01:00
@ if ( $availableAccounts -> isNotEmpty ())
< div class = " mb-4 " >
2026-08-10 19:40:22 +02:00
< h4 class = " text-sm font-medium text-gray-700 mb-2 dark:text-gray-200 " > Available Accounts </ h4 >
2026-01-22 23:38:00 +01:00
< div class = " space-y-2 " >
@ foreach ( $availableAccounts as $account )
2026-08-10 01:43:41 +02:00
< div class = " flex items-center justify-between p-2 border rounded-sm " >
2026-08-10 19:40:22 +02:00
< span class = " text-sm text-gray-900 dark:text-gray-100 " > {{ $account -> username }} </ span >
2026-01-22 23:38:00 +01:00
< button
wire : click = " attachAccount( { { $account->id }}) "
2026-08-10 19:40:22 +02:00
class = " text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 "
2026-01-22 23:38:00 +01:00
>
Add
</ button >
</ div >
@ endforeach
</ div >
</ div >
@ else
2026-08-10 19:40:22 +02:00
< p class = " text-sm text-gray-500 mb-4 dark:text-gray-400 " > No available accounts to link .</ p >
2026-01-22 23:38:00 +01:00
@ endif
< div class = " mt-4 " >
< button
wire : click = " closeAccountModal "
2026-08-10 19:40:22 +02:00
class = " w-full inline-flex justify-center rounded-md border border-gray-300 shadow-xs px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700/50 "
2026-01-22 23:38:00 +01:00
>
Close
</ button >
</ div >
2026-08-01 00:11:06 +02:00
</ x - form - modal >
2026-01-22 23:38:00 +01:00
@ endif
2026-07-25 20:15:52 +02:00
<!-- Create Channel Modal -->
@ if ( $showCreateModal )
2026-08-01 00:11:06 +02:00
< x - form - modal title = " Add Channel " close = " closeCreateModal " >
2026-07-25 20:15:52 +02:00
< form wire : submit = " createChannel " class = " space-y-4 " >
< div >
2026-08-10 19:40:22 +02:00
< label for = " new-channel-instance " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Platform Instance </ label >
2026-07-25 20:15:52 +02:00
< select
id = " new-channel-instance "
2026-08-06 00:20:49 +02:00
wire : model . live = " newPlatformInstanceId "
2026-08-10 19:40:22 +02:00
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
2026-07-25 20:15:52 +02:00
>
< option value = " " > Select an instance </ option >
@ foreach ( $platformInstances as $instance )
< option value = " { { $instance->id }} " > {{ $instance -> name }} </ option >
@ endforeach
</ select >
2026-08-10 19:40:22 +02:00
@ error ( 'newPlatformInstanceId' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
2026-08-06 00:20:49 +02:00
@ if ( $communityLoadError )
2026-08-10 19:40:22 +02:00
< p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $communityLoadError }} </ p >
2026-08-06 00:20:49 +02:00
@ endif
2026-07-25 20:15:52 +02:00
</ div >
2026-08-06 00:20:49 +02:00
@ if ( $availableCommunities )
< div >
< div class = " flex items-center justify-between " >
2026-08-10 19:40:22 +02:00
< label for = " new-channel-community " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Community </ label >
2026-08-06 00:20:49 +02:00
< button
type = " button "
wire : click = " refreshCommunities "
2026-08-10 19:40:22 +02:00
class = " text-xs text-blue-600 hover:text-blue-800 dark:text-blue-400 "
2026-08-06 00:20:49 +02:00
>
Refresh
</ button >
</ div >
< select
id = " new-channel-community "
wire : model = " newCommunityId "
2026-08-10 19:40:22 +02:00
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
2026-08-06 00:20:49 +02:00
>
< option value = " " > Select a community </ option >
@ foreach ( $availableCommunities as $community )
< option value = " { { $community['id'] }} " > {{ $community [ 'title' ] }} ({{ $community [ 'name' ] }}) </ option >
@ endforeach
</ select >
2026-08-10 19:40:22 +02:00
@ error ( 'newCommunityId' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
2026-08-06 00:20:49 +02:00
</ div >
@ endif
2026-07-25 20:15:52 +02:00
< div >
2026-08-10 19:40:22 +02:00
< label for = " new-channel-language " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Language < span class = " text-gray-400 dark:text-gray-500 " > ( optional ) </ span ></ label >
2026-07-25 20:15:52 +02:00
< select
id = " new-channel-language "
wire : model = " newLanguageId "
2026-08-10 19:40:22 +02:00
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
2026-07-25 20:15:52 +02:00
>
< option value = " " > Select a language </ option >
@ foreach ( $languages as $language )
< option value = " { { $language->id }} " > {{ $language -> name }} </ option >
@ endforeach
</ select >
2026-08-10 19:40:22 +02:00
@ error ( 'newLanguageId' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
2026-07-25 20:15:52 +02:00
</ div >
< div >
2026-08-10 19:40:22 +02:00
< label for = " new-channel-description " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Description < span class = " text-gray-400 dark:text-gray-500 " > ( optional ) </ span ></ label >
2026-07-25 20:15:52 +02:00
< textarea
id = " new-channel-description "
wire : model = " newDescription "
rows = " 2 "
2026-08-10 19:40:22 +02:00
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
2026-07-25 20:15:52 +02:00
></ textarea >
2026-08-10 19:40:22 +02:00
@ error ( 'newDescription' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
2026-07-25 20:15:52 +02:00
</ div >
< div class = " mt-6 flex justify-end space-x-3 " >
< button
type = " button "
wire : click = " closeCreateModal "
2026-08-10 19:40:22 +02:00
class = " inline-flex justify-center rounded-md border border-gray-300 shadow-xs px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700/50 "
2026-07-25 20:15:52 +02:00
>
Cancel
</ button >
< button
type = " submit "
wire : loading . attr = " disabled "
wire : target = " createChannel "
2026-08-10 01:46:02 +02:00
class = " inline-flex justify-center rounded-md border border-transparent shadow-xs px-4 py-2 bg-blue-600 text-sm font-medium text-white hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed "
2026-07-25 20:15:52 +02:00
>
Create
</ button >
</ div >
</ form >
2026-08-01 00:11:06 +02:00
</ x - form - modal >
2026-07-25 20:15:52 +02:00
@ endif
2026-08-13 18:44:59 +02:00
<!-- Edit Channel Modal -->
@ if ( $editingChannel )
< x - form - modal title = " Edit Channel " close = " closeEditModal " >
< form wire : submit = " updateChannel " class = " space-y-4 " >
< div >
< label for = " edit-channel-display-name " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Display name </ label >
< input
type = " text "
id = " edit-channel-display-name "
wire : model = " editDisplayName "
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
/>
@ error ( 'editDisplayName' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
</ div >
< div >
< label for = " edit-channel-language " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Language < span class = " text-gray-400 dark:text-gray-500 " > ( optional ) </ span ></ label >
< select
id = " edit-channel-language "
wire : model = " editLanguageId "
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
>
< option value = " " > No language </ option >
@ foreach ( $languages as $language )
< option value = " { { $language->id }} " > {{ $language -> name }} </ option >
@ endforeach
</ select >
@ error ( 'editLanguageId' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
</ div >
< div >
< label for = " edit-channel-description " class = " block text-sm font-medium text-gray-700 dark:text-gray-200 " > Description < span class = " text-gray-400 dark:text-gray-500 " > ( optional ) </ span ></ label >
< textarea
id = " edit-channel-description "
wire : model = " editDescription "
rows = " 2 "
class = " mt-1 block w-full rounded-md border border-gray-300 shadow-xs focus:border-blue-500 focus:ring-blue-500 sm:text-sm dark:border-gray-600 "
></ textarea >
@ error ( 'editDescription' ) < p class = " mt-1 text-sm text-red-600 dark:text-red-400 " > {{ $message }} </ p > @ enderror
</ div >
< div class = " rounded-md bg-gray-50 p-3 dark:bg-gray-700/30 " >
< dl class = " space-y-1 text-sm " >
< div class = " flex justify-between " >
< dt class = " text-gray-500 dark:text-gray-400 " > Community </ dt >
< dd class = " font-medium text-gray-700 dark:text-gray-200 " > {{ $editingChannel -> name }} </ dd >
</ div >
< div class = " flex justify-between " >
< dt class = " text-gray-500 dark:text-gray-400 " > Instance </ dt >
< dd class = " font-medium text-gray-700 dark:text-gray-200 " > {{ $editingChannel -> platformInstance -> name }} </ dd >
</ div >
</ dl >
< p class = " mt-2 text-xs text-gray-500 dark:text-gray-400 " >
The community and instance identify this channel on the remote platform and cannot be changed . Every route attached to this channel publishes here . Create a new channel to post somewhere else .
</ p >
</ div >
< div class = " mt-6 flex justify-end space-x-3 " >
< button
type = " button "
wire : click = " closeEditModal "
class = " inline-flex justify-center rounded-md border border-gray-300 shadow-xs px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700/50 "
>
Cancel
</ button >
< button
type = " submit "
wire : loading . attr = " disabled "
wire : target = " updateChannel "
class = " inline-flex justify-center rounded-md border border-transparent shadow-xs px-4 py-2 bg-blue-600 text-sm font-medium text-white hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed "
>
Save changes
</ button >
</ div >
</ form >
</ x - form - modal >
@ endif
2026-01-22 23:38:00 +01:00
</ div >