Client area translations (#1554)

This commit is contained in:
Charles 2025-08-01 07:26:14 -04:00 committed by GitHub
parent 71225bd2dc
commit 8840d109ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 889 additions and 283 deletions

View File

@ -32,6 +32,6 @@ enum BackupStatus: string implements HasColor, HasIcon, HasLabel
public function getLabel(): string public function getLabel(): string
{ {
return str($this->value)->headline(); return trans('server/backup.backup_status.' . strtolower($this->value));
} }
} }

View File

@ -68,7 +68,7 @@ enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
public function getLabel(): string public function getLabel(): string
{ {
return str($this->value)->title(); return trans('server/console.status.' . strtolower($this->value));
} }
public function isOffline(): bool public function isOffline(): bool

View File

@ -78,21 +78,21 @@ class ListServers extends ListRecords
->copyable(request()->isSecure()) ->copyable(request()->isSecure())
->state(fn (Server $server) => $server->allocation->address ?? 'None'), ->state(fn (Server $server) => $server->allocation->address ?? 'None'),
TextColumn::make('cpuUsage') TextColumn::make('cpuUsage')
->label('Resources') ->label(trans('server/dashboard.resources'))
->icon('tabler-cpu') ->icon('tabler-cpu')
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource(ServerResourceType::CPULimit)) ->tooltip(fn (Server $server) => trans('server/dashboard.usage_limit', ['resource' => $server->formatResource(ServerResourceType::CPULimit)]))
->state(fn (Server $server) => $server->formatResource(ServerResourceType::CPU)) ->state(fn (Server $server) => $server->formatResource(ServerResourceType::CPU))
->color(fn (Server $server) => $this->getResourceColor($server, 'cpu')), ->color(fn (Server $server) => $this->getResourceColor($server, 'cpu')),
TextColumn::make('memoryUsage') TextColumn::make('memoryUsage')
->label('') ->label('')
->icon('tabler-device-desktop-analytics') ->icon('tabler-device-desktop-analytics')
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource(ServerResourceType::MemoryLimit)) ->tooltip(fn (Server $server) => trans('server/dashboard.usage_limit', ['resource' => $server->formatResource(ServerResourceType::MemoryLimit)]))
->state(fn (Server $server) => $server->formatResource(ServerResourceType::Memory)) ->state(fn (Server $server) => $server->formatResource(ServerResourceType::Memory))
->color(fn (Server $server) => $this->getResourceColor($server, 'memory')), ->color(fn (Server $server) => $this->getResourceColor($server, 'memory')),
TextColumn::make('diskUsage') TextColumn::make('diskUsage')
->label('') ->label('')
->icon('tabler-device-sd-card') ->icon('tabler-device-sd-card')
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource(ServerResourceType::DiskLimit)) ->tooltip(fn (Server $server) => trans('server/dashboard.usage_limit', ['resource' => $server->formatResource(ServerResourceType::DiskLimit)]))
->state(fn (Server $server) => $server->formatResource(ServerResourceType::Disk)) ->state(fn (Server $server) => $server->formatResource(ServerResourceType::Disk))
->color(fn (Server $server) => $this->getResourceColor($server, 'disk')), ->color(fn (Server $server) => $this->getResourceColor($server, 'disk')),
]; ];

View File

@ -15,6 +15,11 @@ class PreviewStartupAction extends Action
return 'preview'; return 'preview';
} }
public function getLabel(): string
{
return trans('server/startup.preview');
}
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();

View File

@ -162,6 +162,7 @@ class Console extends Page
return [ return [
Action::make('start') Action::make('start')
->label(trans('server/console.power_actions.start'))
->color('primary') ->color('primary')
->size(ActionSize::ExtraLarge) ->size(ActionSize::ExtraLarge)
->dispatch('setServerState', ['state' => 'start', 'uuid' => $server->uuid]) ->dispatch('setServerState', ['state' => 'start', 'uuid' => $server->uuid])
@ -169,6 +170,7 @@ class Console extends Page
->disabled(fn () => $server->isInConflictState() || !$this->status->isStartable()) ->disabled(fn () => $server->isInConflictState() || !$this->status->isStartable())
->icon('tabler-player-play-filled'), ->icon('tabler-player-play-filled'),
Action::make('restart') Action::make('restart')
->label(trans('server/console.power_actions.restart'))
->color('gray') ->color('gray')
->size(ActionSize::ExtraLarge) ->size(ActionSize::ExtraLarge)
->dispatch('setServerState', ['state' => 'restart', 'uuid' => $server->uuid]) ->dispatch('setServerState', ['state' => 'restart', 'uuid' => $server->uuid])
@ -176,6 +178,7 @@ class Console extends Page
->disabled(fn () => $server->isInConflictState() || !$this->status->isRestartable()) ->disabled(fn () => $server->isInConflictState() || !$this->status->isRestartable())
->icon('tabler-reload'), ->icon('tabler-reload'),
Action::make('stop') Action::make('stop')
->label(trans('server/console.power_actions.stop'))
->color('danger') ->color('danger')
->size(ActionSize::ExtraLarge) ->size(ActionSize::ExtraLarge)
->dispatch('setServerState', ['state' => 'stop', 'uuid' => $server->uuid]) ->dispatch('setServerState', ['state' => 'stop', 'uuid' => $server->uuid])
@ -184,8 +187,9 @@ class Console extends Page
->disabled(fn () => $server->isInConflictState() || !$this->status->isStoppable()) ->disabled(fn () => $server->isInConflictState() || !$this->status->isStoppable())
->icon('tabler-player-stop-filled'), ->icon('tabler-player-stop-filled'),
Action::make('kill') Action::make('kill')
->label(trans('server/console.power_actions.kill'))
->color('danger') ->color('danger')
->tooltip('This can result in data corruption and/or data loss!') ->tooltip(trans('server/console.power_actions.kill_tooltip'))
->size(ActionSize::ExtraLarge) ->size(ActionSize::ExtraLarge)
->dispatch('setServerState', ['state' => 'kill', 'uuid' => $server->uuid]) ->dispatch('setServerState', ['state' => 'kill', 'uuid' => $server->uuid])
->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_CONTROL_STOP, $server))
@ -193,4 +197,14 @@ class Console extends Page
->icon('tabler-alert-square'), ->icon('tabler-alert-square'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/console.title');
}
public function getTitle(): string
{
return trans('server/console.title');
}
} }

View File

@ -39,7 +39,7 @@ class Settings extends ServerFormPage
'lg' => 6, 'lg' => 6,
]) ])
->schema([ ->schema([
Section::make('Server Information') Section::make(trans('server/setting.server_info.title'))
->columns([ ->columns([
'default' => 1, 'default' => 1,
'sm' => 2, 'sm' => 2,
@ -47,11 +47,11 @@ class Settings extends ServerFormPage
'lg' => 6, 'lg' => 6,
]) ])
->schema([ ->schema([
Fieldset::make('Server') Fieldset::make()
->label('Information') ->label(trans('server/setting.server_info.information'))
->schema([ ->schema([
TextInput::make('name') TextInput::make('name')
->label('Server Name') ->label(trans('server/setting.server_info.name'))
->disabled(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server))
->required() ->required()
->columnSpan([ ->columnSpan([
@ -63,7 +63,7 @@ class Settings extends ServerFormPage
->live(onBlur: true) ->live(onBlur: true)
->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)), ->afterStateUpdated(fn ($state, Server $server) => $this->updateName($state, $server)),
Textarea::make('description') Textarea::make('description')
->label('Server Description') ->label(trans('server/setting.server_info.description'))
->hidden(!config('panel.editable_server_descriptions')) ->hidden(!config('panel.editable_server_descriptions'))
->disabled(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_RENAME, $server))
->columnSpan([ ->columnSpan([
@ -76,7 +76,7 @@ class Settings extends ServerFormPage
->live(onBlur: true) ->live(onBlur: true)
->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)), ->afterStateUpdated(fn ($state, Server $server) => $this->updateDescription($state ?? '', $server)),
TextInput::make('uuid') TextInput::make('uuid')
->label('Server UUID') ->label(trans('server/setting.server_info.uuid'))
->columnSpan([ ->columnSpan([
'default' => 1, 'default' => 1,
'sm' => 1, 'sm' => 1,
@ -85,12 +85,12 @@ class Settings extends ServerFormPage
]) ])
->disabled(), ->disabled(),
TextInput::make('id') TextInput::make('id')
->label('Server ID') ->label(trans('server/setting.server_info.id'))
->disabled() ->disabled()
->columnSpan(1), ->columnSpan(1),
]), ]),
Fieldset::make('Limits') Fieldset::make()
->label('Limits') ->label(trans('server/setting.server_info.limits.title'))
->columns([ ->columns([
'default' => 1, 'default' => 1,
'sm' => 1, 'sm' => 1,
@ -100,57 +100,56 @@ class Settings extends ServerFormPage
->schema([ ->schema([
TextInput::make('cpu') TextInput::make('cpu')
->label('') ->label('')
->prefix('CPU') ->prefix(trans('server/setting.server_info.limits.cpu'))
->prefixIcon('tabler-cpu') ->prefixIcon('tabler-cpu')
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->formatStateUsing(fn ($state, Server $server) => !$state ? 'Unlimited' : Number::format($server->cpu, locale: auth()->user()->language) . '%'), ->formatStateUsing(fn ($state, Server $server) => !$state ? trans('server/setting.server_info.limits.unlimited') : Number::format($server->cpu, locale: auth()->user()->language) . '%'),
TextInput::make('memory') TextInput::make('memory')
->label('') ->label('')
->prefix('Memory') ->prefix(trans('server/setting.server_info.limits.memory'))
->prefixIcon('tabler-device-desktop-analytics') ->prefixIcon('tabler-device-desktop-analytics')
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->formatStateUsing(fn ($state, Server $server) => !$state ? 'Unlimited' : convert_bytes_to_readable($server->memory * 2 ** 20)), ->formatStateUsing(fn ($state, Server $server) => !$state ? trans('server/setting.server_info.limits.unlimited') : convert_bytes_to_readable($server->memory * 2 ** 20)),
TextInput::make('disk') TextInput::make('disk')
->label('') ->label('')
->prefix('Disk Space') ->prefix(trans('server/setting.server_info.limits.disk'))
->prefixIcon('tabler-device-sd-card') ->prefixIcon('tabler-device-sd-card')
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->formatStateUsing(fn ($state, Server $server) => !$state ? 'Unlimited' : convert_bytes_to_readable($server->disk * 2 ** 20)), ->formatStateUsing(fn ($state, Server $server) => !$state ? trans('server/setting.server_info.limits.unlimited') : convert_bytes_to_readable($server->disk * 2 ** 20)),
TextInput::make('backup_limit') TextInput::make('backup_limit')
->label('') ->label('')
->prefix('Backups') ->prefix(trans('server/setting.server_info.limits.backups'))
->prefixIcon('tabler-file-zip') ->prefixIcon('tabler-file-zip')
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->formatStateUsing(fn ($state, Server $server) => !$state ? 'No Backups' : $server->backups->count() . ' of ' . $state), ->formatStateUsing(fn ($state, Server $server) => !$state ? 'No Backups' : $server->backups->count() . ' ' .trans('server/setting.server_info.limits.of') . ' ' . $state),
TextInput::make('database_limit') TextInput::make('database_limit')
->label('') ->label('')
->prefix('Databases') ->prefix(trans('server/setting.server_info.limits.databases'))
->prefixIcon('tabler-database') ->prefixIcon('tabler-database')
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->formatStateUsing(fn ($state, Server $server) => !$state ? 'No Databases' : $server->databases->count() . ' of ' . $state), ->formatStateUsing(fn ($state, Server $server) => !$state ? 'No Databases' : $server->databases->count() . ' ' . trans('server/setting.server_info.limits.of') . ' ' .$state),
TextInput::make('allocation_limit') TextInput::make('allocation_limit')
->label('') ->label('')
->prefix('Allocations') ->prefix(trans('server/setting.server_info.limits.allocations'))
->prefixIcon('tabler-network') ->prefixIcon('tabler-network')
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->formatStateUsing(fn ($state, Server $server) => !$state ? 'No Additional Allocations' : $server->allocations->count() . ' of ' . $state), ->formatStateUsing(fn ($state, Server $server) => !$state ? trans('server/setting.server_info.limits.no_allocations') : $server->allocations->count() . ' ' .trans('server/setting.server_info.limits.of') . ' ' . $state),
]), ]),
]), ]),
Section::make('Node Information') Section::make(trans('server/setting.node_info.title'))
->schema([ ->schema([
TextInput::make('node.name') TextInput::make('node.name')
->label('Node Name') ->label(trans('server/setting.node_info.name'))
->formatStateUsing(fn (Server $server) => $server->node->name) ->formatStateUsing(fn (Server $server) => $server->node->name)
->disabled(), ->disabled(),
Fieldset::make('SFTP Information') Fieldset::make(trans('server/setting.node_info.sftp.title'))
->hidden(fn () => !auth()->user()->can(Permission::ACTION_FILE_SFTP, $server)) ->hidden(fn () => !auth()->user()->can(Permission::ACTION_FILE_SFTP, $server))
->label('SFTP Information')
->columns([ ->columns([
'default' => 1, 'default' => 1,
'sm' => 1, 'sm' => 1,
@ -159,13 +158,13 @@ class Settings extends ServerFormPage
]) ])
->schema([ ->schema([
TextInput::make('connection') TextInput::make('connection')
->label('Connection') ->label(trans('server/setting.node_info.sftp.connection'))
->columnSpan(1) ->columnSpan(1)
->disabled() ->disabled()
->suffixAction(fn () => request()->isSecure() ? CopyAction::make() : null) ->suffixAction(fn () => request()->isSecure() ? CopyAction::make() : null)
->hintAction( ->hintAction(
Action::make('connect_sftp') Action::make('connect_sftp')
->label('Connect to SFTP') ->label(trans('server/setting.node_info.sftp.action'))
->color('success') ->color('success')
->icon('tabler-plug') ->icon('tabler-plug')
->url(function (Server $server) { ->url(function (Server $server) {
@ -180,28 +179,29 @@ class Settings extends ServerFormPage
return 'sftp://' . auth()->user()->username . '.' . $server->uuid_short . '@' . $fqdn . ':' . $server->node->daemon_sftp; return 'sftp://' . auth()->user()->username . '.' . $server->uuid_short . '@' . $fqdn . ':' . $server->node->daemon_sftp;
}), }),
TextInput::make('username') TextInput::make('username')
->label('Username') ->label(trans('server/setting.node_info.sftp.username'))
->columnSpan(1) ->columnSpan(1)
->suffixAction(fn () => request()->isSecure() ? CopyAction::make() : null) ->suffixAction(fn () => request()->isSecure() ? CopyAction::make() : null)
->disabled() ->disabled()
->formatStateUsing(fn (Server $server) => auth()->user()->username . '.' . $server->uuid_short), ->formatStateUsing(fn (Server $server) => auth()->user()->username . '.' . $server->uuid_short),
Placeholder::make('password') Placeholder::make('password')
->label(trans('server/setting.node_info.sftp.password'))
->columnSpan(1) ->columnSpan(1)
->content('Your SFTP password is the same as the password you use to access this panel.'), ->content(trans('server/setting.node_info.sftp.password_body')),
]), ]),
]), ]),
Section::make('Reinstall Server') Section::make(trans('server/setting.reinstall.title'))
->hidden(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server)) ->hidden(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
->collapsible() ->collapsible()
->footerActions([ ->footerActions([
Action::make('reinstall') Action::make('reinstall')
->label(trans('server/setting.reinstall.action'))
->color('danger') ->color('danger')
->disabled(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server))
->label('Reinstall')
->requiresConfirmation() ->requiresConfirmation()
->modalHeading('Are you sure you want to reinstall the server?') ->modalHeading(trans('server/setting.reinstall.modal'))
->modalDescription('Some files may be deleted or modified during this process, please back up your data before continuing.') ->modalDescription(trans('server/setting.reinstall.modal_description'))
->modalSubmitActionLabel('Yes, Reinstall') ->modalSubmitActionLabel(trans('server/setting.reinstall.yes'))
->action(function (Server $server, ReinstallServerService $reinstallService) { ->action(function (Server $server, ReinstallServerService $reinstallService) {
abort_unless(auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server), 403); abort_unless(auth()->user()->can(Permission::ACTION_SETTINGS_REINSTALL, $server), 403);
@ -211,9 +211,9 @@ class Settings extends ServerFormPage
report($exception); report($exception);
Notification::make() Notification::make()
->danger() ->title(trans('server/setting.reinstall.notification_fail'))
->title('Server Reinstall failed')
->body($exception->getMessage()) ->body($exception->getMessage())
->danger()
->send(); ->send();
return; return;
@ -223,8 +223,8 @@ class Settings extends ServerFormPage
->log(); ->log();
Notification::make() Notification::make()
->title(trans('server/setting.reinstall.notification_start'))
->success() ->success()
->title('Server Reinstall started')
->send(); ->send();
redirect(Console::getUrl()); redirect(Console::getUrl());
@ -233,9 +233,9 @@ class Settings extends ServerFormPage
->footerActionsAlignment(Alignment::Right) ->footerActionsAlignment(Alignment::Right)
->schema([ ->schema([
Placeholder::make('') Placeholder::make('')
->label('Reinstalling your server will stop it, and then re-run the installation script that initially set it up.'), ->label(trans('server/setting.reinstall.body')),
Placeholder::make('') Placeholder::make('')
->label('Some files may be deleted or modified during this process, please back up your data before continuing.'), ->label(trans('server/setting.reinstall.body2')),
]), ]),
]); ]);
} }
@ -258,15 +258,15 @@ class Settings extends ServerFormPage
} }
Notification::make() Notification::make()
->success() ->title(trans('server/setting.notification_name'))
->title('Updated Server Name')
->body(fn () => $original . ' -> ' . $name) ->body(fn () => $original . ' -> ' . $name)
->success()
->send(); ->send();
} catch (Exception $exception) { } catch (Exception $exception) {
Notification::make() Notification::make()
->danger() ->title(trans('server/setting.failed'))
->title('Failed')
->body($exception->getMessage()) ->body($exception->getMessage())
->danger()
->send(); ->send();
} }
} }
@ -289,16 +289,26 @@ class Settings extends ServerFormPage
} }
Notification::make() Notification::make()
->success() ->title(trans('server/setting.notification_description'))
->title('Updated Server Description')
->body(fn () => $original . ' -> ' . $description) ->body(fn () => $original . ' -> ' . $description)
->success()
->send(); ->send();
} catch (Exception $exception) { } catch (Exception $exception) {
Notification::make() Notification::make()
->danger() ->title(trans('server/setting.failed'))
->title('Failed')
->body($exception->getMessage()) ->body($exception->getMessage())
->danger()
->send(); ->send();
} }
} }
public function getTitle(): string
{
return trans('server/setting.title');
}
public static function getNavigationLabel(): string
{
return trans('server/setting.title');
}
} }

View File

@ -18,6 +18,7 @@ use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@ -43,7 +44,7 @@ class Startup extends ServerFormPage
Hidden::make('previewing') Hidden::make('previewing')
->default(false), ->default(false),
Textarea::make('startup') Textarea::make('startup')
->label('Startup Command') ->label(trans('server/startup.command'))
->columnSpan([ ->columnSpan([
'default' => 1, 'default' => 1,
'sm' => 1, 'sm' => 1,
@ -51,10 +52,10 @@ class Startup extends ServerFormPage
'lg' => 4, 'lg' => 4,
]) ])
->autosize() ->autosize()
->hintAction(PreviewStartupAction::make('preview')) ->hintAction(PreviewStartupAction::make())
->readOnly(), ->readOnly(),
TextInput::make('custom_image') TextInput::make('custom_image')
->label('Docker Image') ->label(trans('server/startup.docker_image'))
->readOnly() ->readOnly()
->visible(fn (Server $server) => !in_array($server->image, $server->egg->docker_images)) ->visible(fn (Server $server) => !in_array($server->image, $server->egg->docker_images))
->formatStateUsing(fn (Server $server) => $server->image) ->formatStateUsing(fn (Server $server) => $server->image)
@ -65,7 +66,7 @@ class Startup extends ServerFormPage
'lg' => 2, 'lg' => 2,
]), ]),
Select::make('image') Select::make('image')
->label('Docker Image') ->label(trans('server/startup.docker_image'))
->live() ->live()
->visible(fn (Server $server) => in_array($server->image, $server->egg->docker_images)) ->visible(fn (Server $server) => in_array($server->image, $server->egg->docker_images))
->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_DOCKER_IMAGE, $server))
@ -80,8 +81,8 @@ class Startup extends ServerFormPage
} }
Notification::make() Notification::make()
->title('Docker image updated') ->title(trans('server/startup.notification_docker'))
->body('Restart the server to use the new image.') ->body(trans('server/startup.notification_docker_body'))
->success() ->success()
->send(); ->send();
}) })
@ -97,10 +98,10 @@ class Startup extends ServerFormPage
'md' => 2, 'md' => 2,
'lg' => 2, 'lg' => 2,
]), ]),
Section::make('Server Variables') Section::make(trans('server/startup.variables'))
->schema([ ->schema([
Repeater::make('server_variables') Repeater::make('server_variables')
->label('') ->hiddenLabel()
->relationship('serverVariables', fn (Builder $query) => $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort')) ->relationship('serverVariables', fn (Builder $query) => $query->where('egg_variables.user_viewable', true)->orderByPowerJoins('variable.sort'))
->grid() ->grid()
->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_STARTUP_UPDATE, $server))
@ -207,9 +208,9 @@ class Startup extends ServerFormPage
if ($validator->fails()) { if ($validator->fails()) {
Notification::make() Notification::make()
->danger() ->title(trans('server/startup.validation_fail', ['variable' => $serverVariable->variable->name]))
->title('Validation Failed: ' . $serverVariable->variable->name)
->body(implode(', ', $validator->errors()->all())) ->body(implode(', ', $validator->errors()->all()))
->danger()
->send(); ->send();
return null; return null;
@ -232,18 +233,28 @@ class Startup extends ServerFormPage
->log(); ->log();
} }
Notification::make() Notification::make()
->success() ->title(trans('server/startup.update', ['variable' => $serverVariable->variable->name]))
->title('Updated: ' . $serverVariable->variable->name)
->body(fn () => $original . ' -> ' . $state) ->body(fn () => $original . ' -> ' . $state)
->success()
->send(); ->send();
} catch (\Exception $e) { } catch (\Exception $e) {
Notification::make() Notification::make()
->danger() ->title(trans('server/startup.fail', ['variable' => $serverVariable->variable->name]))
->title('Failed: ' . $serverVariable->variable->name)
->body($e->getMessage()) ->body($e->getMessage())
->danger()
->send(); ->send();
} }
return null; return null;
} }
public function getTitle(): string|Htmlable
{
return trans('server/startup.title');
}
public static function getNavigationLabel(): string
{
return trans('server/startup.title');
}
} }

View File

@ -38,10 +38,6 @@ class ActivityResource extends Resource
protected static ?string $model = ActivityLog::class; protected static ?string $model = ActivityLog::class;
protected static ?string $modelLabel = 'Activity';
protected static ?string $pluralModelLabel = 'Activity';
protected static ?int $navigationSort = 8; protected static ?int $navigationSort = 8;
protected static ?string $navigationIcon = 'tabler-stack'; protected static ?string $navigationIcon = 'tabler-stack';
@ -56,14 +52,16 @@ class ActivityResource extends Resource
->defaultPaginationPageOption(25) ->defaultPaginationPageOption(25)
->columns([ ->columns([
TextColumn::make('event') TextColumn::make('event')
->label(trans('server/activity.event'))
->html() ->html()
->description(fn ($state) => $state) ->description(fn ($state) => $state)
->icon(fn (ActivityLog $activityLog) => $activityLog->getIcon()) ->icon(fn (ActivityLog $activityLog) => $activityLog->getIcon())
->formatStateUsing(fn (ActivityLog $activityLog) => $activityLog->getLabel()), ->formatStateUsing(fn (ActivityLog $activityLog) => $activityLog->getLabel()),
TextColumn::make('user') TextColumn::make('user')
->label(trans('server/activity.user'))
->state(function (ActivityLog $activityLog) use ($server) { ->state(function (ActivityLog $activityLog) use ($server) {
if (!$activityLog->actor instanceof User) { if (!$activityLog->actor instanceof User) {
return $activityLog->actor_id === null ? 'System' : 'Deleted user'; return $activityLog->actor_id === null ? trans('server/activity.system') : trans('server/activity.deleted_user');
} }
$user = $activityLog->actor->username; $user = $activityLog->actor->username;
@ -79,6 +77,7 @@ class ActivityResource extends Resource
->url(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update', $activityLog->actor) ? EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin') : '') ->url(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update', $activityLog->actor) ? EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin') : '')
->grow(false), ->grow(false),
DateTimeColumn::make('timestamp') DateTimeColumn::make('timestamp')
->label(trans('server/activity.timestamp'))
->since() ->since()
->sortable() ->sortable()
->grow(false), ->grow(false),
@ -89,11 +88,13 @@ class ActivityResource extends Resource
//->visible(fn (ActivityLog $activityLog) => $activityLog->hasAdditionalMetadata()) //->visible(fn (ActivityLog $activityLog) => $activityLog->hasAdditionalMetadata())
->form([ ->form([
Placeholder::make('event') Placeholder::make('event')
->label(trans('server/activity.event'))
->content(fn (ActivityLog $activityLog) => new HtmlString($activityLog->getLabel())), ->content(fn (ActivityLog $activityLog) => new HtmlString($activityLog->getLabel())),
TextInput::make('user') TextInput::make('user')
->label(trans('server/activity.user'))
->formatStateUsing(function (ActivityLog $activityLog) use ($server) { ->formatStateUsing(function (ActivityLog $activityLog) use ($server) {
if (!$activityLog->actor instanceof User) { if (!$activityLog->actor instanceof User) {
return $activityLog->actor_id === null ? 'System' : 'Deleted user'; return $activityLog->actor_id === null ? trans('server/activity.system') : trans('server/activity.deleted_user');
} }
$user = $activityLog->actor->username; $user = $activityLog->actor->username;
@ -116,9 +117,10 @@ class ActivityResource extends Resource
->visible(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update', $activityLog->actor)) ->visible(fn (ActivityLog $activityLog) => $activityLog->actor instanceof User && auth()->user()->can('update', $activityLog->actor))
->url(fn (ActivityLog $activityLog) => EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin')) ->url(fn (ActivityLog $activityLog) => EditUser::getUrl(['record' => $activityLog->actor], panel: 'admin'))
), ),
DateTimePicker::make('timestamp'), DateTimePicker::make('timestamp')
->label(trans('server/activity.timestamp')),
KeyValue::make('properties') KeyValue::make('properties')
->label('Metadata') ->label(trans('server/activity.metadata'))
->formatStateUsing(fn ($state) => Arr::dot($state)), ->formatStateUsing(fn ($state) => Arr::dot($state)),
]), ]),
]) ])
@ -168,4 +170,9 @@ class ActivityResource extends Resource
'index' => Pages\ListActivities::route('/'), 'index' => Pages\ListActivities::route('/'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/activity.title');
}
} }

View File

@ -18,4 +18,9 @@ class ListActivities extends ListRecords
{ {
return []; return [];
} }
public function getTitle(): string
{
return trans('server/activity.title');
}
} }

View File

@ -30,10 +30,6 @@ class AllocationResource extends Resource
protected static ?string $model = Allocation::class; protected static ?string $model = Allocation::class;
protected static ?string $modelLabel = 'Network';
protected static ?string $pluralModelLabel = 'Network';
protected static ?int $navigationSort = 7; protected static ?int $navigationSort = 7;
protected static ?string $navigationIcon = 'tabler-network'; protected static ?string $navigationIcon = 'tabler-network';
@ -46,16 +42,17 @@ class AllocationResource extends Resource
return $table return $table
->columns([ ->columns([
TextColumn::make('ip') TextColumn::make('ip')
->label('Address') ->label(trans('server/network.address'))
->formatStateUsing(fn (Allocation $allocation) => $allocation->alias), ->formatStateUsing(fn (Allocation $allocation) => $allocation->alias),
TextColumn::make('alias') TextColumn::make('alias')
->hidden(), ->hidden(),
TextColumn::make('port'), TextColumn::make('port')
->label(trans('server/network.port')),
TextInputColumn::make('notes') TextInputColumn::make('notes')
->label(trans('server/network.notes'))
->visibleFrom('sm') ->visibleFrom('sm')
->disabled(fn () => !auth()->user()->can(Permission::ACTION_ALLOCATION_UPDATE, $server)) ->disabled(fn () => !auth()->user()->can(Permission::ACTION_ALLOCATION_UPDATE, $server))
->label('Notes') ->placeholder(trans('server/network.no_notes')),
->placeholder('No Notes'),
IconColumn::make('primary') IconColumn::make('primary')
->icon(fn ($state) => match ($state) { ->icon(fn ($state) => match ($state) {
true => 'tabler-star-filled', true => 'tabler-star-filled',
@ -65,15 +62,15 @@ class AllocationResource extends Resource
true => 'warning', true => 'warning',
default => 'gray', default => 'gray',
}) })
->tooltip(fn (Allocation $allocation) => ($allocation->id === $server->allocation_id ? 'Already' : 'Make') . ' Primary') ->tooltip(fn (Allocation $allocation) => $allocation->id === $server->allocation_id ? trans('server/network.primary') : trans('server/network.make_primary'))
->action(fn (Allocation $allocation) => auth()->user()->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id])) ->action(fn (Allocation $allocation) => auth()->user()->can(PERMISSION::ACTION_ALLOCATION_UPDATE, $server) && $server->update(['allocation_id' => $allocation->id]))
->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id) ->default(fn (Allocation $allocation) => $allocation->id === $server->allocation_id)
->label('Primary'), ->label(trans('server/network.primary')),
]) ])
->actions([ ->actions([
DetachAction::make() DetachAction::make()
->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_DELETE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_DELETE, $server))
->label('Delete') ->label(trans('server/network.delete'))
->icon('tabler-trash') ->icon('tabler-trash')
->action(function (Allocation $allocation) { ->action(function (Allocation $allocation) {
Allocation::query()->where('id', $allocation->id)->update([ Allocation::query()->where('id', $allocation->id)->update([
@ -117,4 +114,9 @@ class AllocationResource extends Resource
'index' => Pages\ListAllocations::route('/'), 'index' => Pages\ListAllocations::route('/'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/network.title');
}
} }

View File

@ -33,7 +33,7 @@ class ListAllocations extends ListRecords
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network') ->icon(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'tabler-network-off' : 'tabler-network')
->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_ALLOCATION_CREATE, $server))
->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'Allocation limit reached' : 'Add Allocation') ->tooltip(fn () => $server->allocations()->count() >= $server->allocation_limit ? trans('server/network.limit') : trans('server/network.add'))
->hidden(fn () => !config('panel.client_features.allocations.enabled')) ->hidden(fn () => !config('panel.client_features.allocations.enabled'))
->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit) ->disabled(fn () => $server->allocations()->count() >= $server->allocation_limit)
->color(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'danger' : 'primary') ->color(fn () => $server->allocations()->count() >= $server->allocation_limit ? 'danger' : 'primary')
@ -56,4 +56,14 @@ class ListAllocations extends ListRecords
{ {
return []; return [];
} }
public function getTitle(): string
{
return trans('server/network.title');
}
public static function getNavigationLabel(): string
{
return trans('server/network.title');
}
} }

View File

@ -79,14 +79,15 @@ class BackupResource extends Resource
return $form return $form
->schema([ ->schema([
TextInput::make('name') TextInput::make('name')
->label('Name') ->label(trans('server/backup.actions.create.name'))
->columnSpanFull(), ->columnSpanFull(),
TextArea::make('ignored') TextArea::make('ignored')
->columnSpanFull() ->label(trans('server/backup.actions.create.ignored'))
->label('Ignored Files & Directories'), ->columnSpanFull(),
Toggle::make('is_locked') Toggle::make('is_locked')
->label('Lock?') ->label(trans('server/backup.actions.create.locked'))
->helperText('Prevents this backup from being deleted until explicitly unlocked.'), ->helperText(trans('server/backup.actions.create.lock_helper'))
->columnSpanFull(),
]); ]);
} }
@ -98,19 +99,20 @@ class BackupResource extends Resource
return $table return $table
->columns([ ->columns([
TextColumn::make('name') TextColumn::make('name')
->label(trans('server/backup.actions.create.name'))
->searchable(), ->searchable(),
BytesColumn::make('bytes') BytesColumn::make('bytes')
->label('Size'), ->label(trans('server/backup.size')),
DateTimeColumn::make('created_at') DateTimeColumn::make('created_at')
->label('Created') ->label(trans('server/backup.created_at'))
->since() ->since()
->sortable(), ->sortable(),
TextColumn::make('status') TextColumn::make('status')
->label('Status') ->label(trans('server/backup.status'))
->badge(), ->badge(),
IconColumn::make('is_locked') IconColumn::make('is_locked')
->label(trans('server/backup.is_locked'))
->visibleFrom('md') ->visibleFrom('md')
->label('Lock Status')
->trueIcon('tabler-lock') ->trueIcon('tabler-lock')
->falseIcon('tabler-lock-open'), ->falseIcon('tabler-lock-open'),
]) ])
@ -150,39 +152,41 @@ class BackupResource extends Resource
Action::make('lock') Action::make('lock')
->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open') ->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server))
->label(fn (Backup $backup) => !$backup->is_locked ? 'Lock' : 'Unlock') ->label(fn (Backup $backup) => !$backup->is_locked ? trans('server/backup.actions.lock.lock') : trans('server/backup.actions.lock.unlock'))
->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup)) ->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup))
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
Action::make('download') Action::make('download')
->label(trans('server/backup.actions.download'))
->color('primary') ->color('primary')
->icon('tabler-download') ->icon('tabler-download')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server))
->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true) ->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true)
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
Action::make('restore') Action::make('restore')
->label(trans('server/backup.actions.restore.title'))
->color('success') ->color('success')
->icon('tabler-folder-up') ->icon('tabler-folder-up')
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_RESTORE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_RESTORE, $server))
->form([ ->form([
Placeholder::make('') Placeholder::make('')
->helperText('Your server will be stopped. You will not be able to control the power state, access the file manager, or create additional backups until this process is completed.'), ->helperText(trans('server/backup.actions.restore.helper')),
Checkbox::make('truncate') Checkbox::make('truncate')
->label('Delete all files before restoring backup?'), ->label(trans('server/backup.actions.restore.delete_all')),
]) ])
->action(function (Backup $backup, $data, DaemonBackupRepository $daemonRepository, DownloadLinkService $downloadLinkService) use ($server) { ->action(function (Backup $backup, $data, DaemonBackupRepository $daemonRepository, DownloadLinkService $downloadLinkService) use ($server) {
if (!is_null($server->status)) { if (!is_null($server->status)) {
return Notification::make() return Notification::make()
->title(trans('server/backup.actions.restore.notification_fail'))
->body(trans('server/backup.actions.restore.notification_fail_body_1'))
->danger() ->danger()
->title('Backup Restore Failed')
->body('This server is not currently in a state that allows for a backup to be restored.')
->send(); ->send();
} }
if (!$backup->is_successful && is_null($backup->completed_at)) { if (!$backup->is_successful && is_null($backup->completed_at)) {
return Notification::make() return Notification::make()
->title(trans('server/backup.actions.restore.notification_fail'))
->body(trans('server/backup.actions.restore.notification_fail_body_2'))
->danger() ->danger()
->title('Backup Restore Failed')
->body('This backup cannot be restored at this time: not completed or failed.')
->send(); ->send();
} }
@ -205,21 +209,26 @@ class BackupResource extends Resource
}); });
return Notification::make() return Notification::make()
->title('Restoring Backup') ->title(trans('server/backup.actions.restore.notification_started'))
->send(); ->send();
}) })
->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful), ->visible(fn (Backup $backup) => $backup->status === BackupStatus::Successful),
DeleteAction::make('delete') DeleteAction::make('delete')
->disabled(fn (Backup $backup) => $backup->is_locked) ->disabled(fn (Backup $backup) => $backup->is_locked)
->modalDescription(fn (Backup $backup) => 'Do you wish to delete ' . $backup->name . '?') ->modalDescription(fn (Backup $backup) => trans('server/backup.actions.delete.description', ['backup' => $backup->name]))
->modalSubmitActionLabel('Delete Backup') ->modalSubmitActionLabel(trans('server/backup.actions.delete.title'))
->action(function (Backup $backup, DeleteBackupService $deleteBackupService) { ->action(function (Backup $backup, DeleteBackupService $deleteBackupService) {
try { try {
$deleteBackupService->handle($backup); $deleteBackupService->handle($backup);
Notification::make()
->title(trans('server/backup.actions.delete.notification_success'))
->success()
->send();
} catch (ConnectionException) { } catch (ConnectionException) {
Notification::make() Notification::make()
->title('Could not delete backup') ->title(trans('server/backup.actions.delete.notification_fail'))
->body('Connection to node failed') ->body(trans('server/backup.actions.delete.notification_fail_body'))
->danger() ->danger()
->send(); ->send();
@ -258,4 +267,9 @@ class BackupResource extends Resource
'index' => Pages\ListBackups::route('/'), 'index' => Pages\ListBackups::route('/'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/backup.title');
}
} }

View File

@ -36,7 +36,7 @@ class ListBackups extends ListRecords
->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_CREATE, $server))
->icon('tabler-file-zip')->iconButton()->iconSize(IconSize::Large) ->icon('tabler-file-zip')->iconButton()->iconSize(IconSize::Large)
->disabled(fn () => $server->backups()->count() >= $server->backup_limit) ->disabled(fn () => $server->backups()->count() >= $server->backup_limit)
->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? 'Backup Limit Reached' : 'Create Backup') // Disabled Buttons have no tooltips in v3 :/ ->tooltip(fn () => $server->backups()->count() >= $server->backup_limit ? trans('server/backup.actions.create.limit') : trans('server/backup.actions.create.title'))
->color(fn () => $server->backups()->count() >= $server->backup_limit ? 'danger' : 'primary') ->color(fn () => $server->backups()->count() >= $server->backup_limit ? 'danger' : 'primary')
->createAnother(false) ->createAnother(false)
->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) { ->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) {
@ -55,15 +55,15 @@ class ListBackups extends ListRecords
->log(); ->log();
return Notification::make() return Notification::make()
->title('Backup Created') ->title(trans('server/backup.actions.create.notification_success'))
->body($backup->name . ' created.') ->body(trans('server/backup.actions.create.created', ['name' => $backup->name]))
->success() ->success()
->send(); ->send();
} catch (HttpException $e) { } catch (HttpException $e) {
return Notification::make() return Notification::make()
->danger() ->title(trans('server/backup.actions.create.notification_fail'))
->title('Backup Failed')
->body($e->getMessage() . ' Try again' . ($e->getHeaders()['Retry-After'] ? ' in ' . $e->getHeaders()['Retry-After'] . ' seconds.' : '')) ->body($e->getMessage() . ' Try again' . ($e->getHeaders()['Retry-After'] ? ' in ' . $e->getHeaders()['Retry-After'] . ' seconds.' : ''))
->danger()
->send(); ->send();
} }
}), }),
@ -74,4 +74,9 @@ class ListBackups extends ListRecords
{ {
return []; return [];
} }
public function getTitle(): string
{
return trans('server/backup.title');
}
} }

View File

@ -66,13 +66,17 @@ class DatabaseResource extends Resource
return $form return $form
->schema([ ->schema([
TextInput::make('host') TextInput::make('host')
->label(trans('server/database.host'))
->formatStateUsing(fn (Database $database) => $database->address()) ->formatStateUsing(fn (Database $database) => $database->address())
->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null), ->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null),
TextInput::make('database') TextInput::make('database')
->label(trans('server/database.database'))
->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null), ->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null),
TextInput::make('username') TextInput::make('username')
->label(trans('server/database.username'))
->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null), ->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null),
TextInput::make('password') TextInput::make('password')
->label(trans('server/database.password'))
->password()->revealable() ->password()->revealable()
->hidden(fn () => !auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server)) ->hidden(fn () => !auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
->hintAction( ->hintAction(
@ -82,11 +86,12 @@ class DatabaseResource extends Resource
->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null) ->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null)
->formatStateUsing(fn (Database $database) => $database->password), ->formatStateUsing(fn (Database $database) => $database->password),
TextInput::make('remote') TextInput::make('remote')
->label('Connections From'), ->label(trans('server/database.remote')),
TextInput::make('max_connections') TextInput::make('max_connections')
->label(trans('server/database.max_connections'))
->formatStateUsing(fn (Database $database) => $database->max_connections === 0 ? $database->max_connections : 'Unlimited'), ->formatStateUsing(fn (Database $database) => $database->max_connections === 0 ? $database->max_connections : 'Unlimited'),
TextInput::make('jdbc') TextInput::make('jdbc')
->label('JDBC Connection String') ->label(trans('server/database.jdbc'))
->password()->revealable() ->password()->revealable()
->hidden(!auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server)) ->hidden(!auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server))
->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null) ->suffixAction(fn (string $state) => request()->isSecure() ? CopyAction::make()->copyable($state) : null)
@ -100,12 +105,17 @@ class DatabaseResource extends Resource
return $table return $table
->columns([ ->columns([
TextColumn::make('host') TextColumn::make('host')
->label(trans('server/database.host'))
->state(fn (Database $database) => $database->address()) ->state(fn (Database $database) => $database->address())
->badge(), ->badge(),
TextColumn::make('database'), TextColumn::make('database')
TextColumn::make('username'), ->label(trans('server/database.database')),
TextColumn::make('remote'), TextColumn::make('username')
->label(trans('server/database.username')),
TextColumn::make('remote')
->label(trans('server/database.remote')),
DateTimeColumn::make('created_at') DateTimeColumn::make('created_at')
->label(trans('server/database.created_at'))
->sortable(), ->sortable(),
]) ])
->actions([ ->actions([
@ -148,4 +158,9 @@ class DatabaseResource extends Resource
'index' => Pages\ListDatabases::route('/'), 'index' => Pages\ListDatabases::route('/'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/database.title');
}
} }

View File

@ -35,7 +35,7 @@ class ListDatabases extends ListRecords
CreateAction::make('new') CreateAction::make('new')
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon(fn () => $server->databases()->count() >= $server->database_limit ? 'tabler-database-x' : 'tabler-database-plus') ->icon(fn () => $server->databases()->count() >= $server->database_limit ? 'tabler-database-x' : 'tabler-database-plus')
->tooltip(fn () => $server->databases()->count() >= $server->database_limit ? 'Database limit reached' : 'Create Database') ->tooltip(fn () => $server->databases()->count() >= $server->database_limit ? trans('server/database.limit') : trans('server/database.create_database'))
->disabled(fn () => $server->databases()->count() >= $server->database_limit) ->disabled(fn () => $server->databases()->count() >= $server->database_limit)
->color(fn () => $server->databases()->count() >= $server->database_limit ? 'danger' : 'primary') ->color(fn () => $server->databases()->count() >= $server->database_limit ? 'danger' : 'primary')
->createAnother(false) ->createAnother(false)
@ -44,20 +44,20 @@ class ListDatabases extends ListRecords
->columns(2) ->columns(2)
->schema([ ->schema([
Select::make('database_host_id') Select::make('database_host_id')
->label('Database Host') ->label(trans('server/database.database_host'))
->columnSpan(2) ->columnSpan(2)
->required() ->required()
->placeholder('Select Database Host') ->placeholder(trans('server/database.database_host_select'))
->options(fn () => $server->node->databaseHosts->mapWithKeys(fn (DatabaseHost $databaseHost) => [$databaseHost->id => $databaseHost->name])), ->options(fn () => $server->node->databaseHosts->mapWithKeys(fn (DatabaseHost $databaseHost) => [$databaseHost->id => $databaseHost->name])),
TextInput::make('database') TextInput::make('database')
->label(trans('server/database.name'))
->columnSpan(1) ->columnSpan(1)
->label('Database Name')
->prefix('s'. $server->id . '_') ->prefix('s'. $server->id . '_')
->hintIcon('tabler-question-mark') ->hintIcon('tabler-question-mark')
->hintIconTooltip('Leaving this blank will auto generate a random name'), ->hintIconTooltip(trans('server/database.name_hint')),
TextInput::make('remote') TextInput::make('remote')
->label(trans('server/database.connections_from'))
->columnSpan(1) ->columnSpan(1)
->label('Connections From')
->default('%'), ->default('%'),
]), ]),
]) ])
@ -76,4 +76,9 @@ class ListDatabases extends ListRecords
{ {
return []; return [];
} }
public function getTitle(): string
{
return trans('server/database.title');
}
} }

View File

@ -55,4 +55,9 @@ class FileResource extends Resource
'index' => Pages\ListFiles::route('/{path?}'), 'index' => Pages\ListFiles::route('/{path?}'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/file.title');
}
} }

View File

@ -69,10 +69,10 @@ class EditFiles extends Page
return $form return $form
->schema([ ->schema([
Section::make('Editing: ' . $this->path) Section::make(trans('server/file.actions.edit.title', ['file' => $this->path]))
->footerActions([ ->footerActions([
Action::make('save_and_close') Action::make('save_and_close')
->label('Save & Close') ->label(trans('server/file.actions.edit.save_close'))
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
->icon('tabler-device-floppy') ->icon('tabler-device-floppy')
->keyBindings('mod+shift+s') ->keyBindings('mod+shift+s')
@ -85,14 +85,14 @@ class EditFiles extends Page
Notification::make() Notification::make()
->success() ->success()
->title('File saved') ->title(trans('server/file.actions.edit.notification'))
->body(fn () => $this->path) ->body(fn () => $this->path)
->send(); ->send();
$this->redirect(ListFiles::getUrl(['path' => dirname($this->path)])); $this->redirect(ListFiles::getUrl(['path' => dirname($this->path)]));
}), }),
Action::make('save') Action::make('save')
->label('Save') ->label(trans('server/file.actions.edit.save'))
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
->icon('tabler-device-floppy') ->icon('tabler-device-floppy')
->keyBindings('mod+s') ->keyBindings('mod+s')
@ -105,12 +105,12 @@ class EditFiles extends Page
Notification::make() Notification::make()
->success() ->success()
->title('File saved') ->title(trans('server/file.actions.edit.notification'))
->body(fn () => $this->path) ->body(fn () => $this->path)
->send(); ->send();
}), }),
Action::make('cancel') Action::make('cancel')
->label('Cancel') ->label(trans('server/file.actions.edit.cancel'))
->color('danger') ->color('danger')
->icon('tabler-x') ->icon('tabler-x')
->url(fn () => ListFiles::getUrl(['path' => dirname($this->path)])), ->url(fn () => ListFiles::getUrl(['path' => dirname($this->path)])),
@ -118,7 +118,7 @@ class EditFiles extends Page
->footerActionsAlignment(Alignment::End) ->footerActionsAlignment(Alignment::End)
->schema([ ->schema([
Select::make('lang') Select::make('lang')
->label('Syntax Highlighting') ->label(trans('server/file.actions.new_file.syntax'))
->searchable() ->searchable()
->native(false) ->native(false)
->live() ->live()

View File

@ -90,14 +90,17 @@ class ListFiles extends ListRecords
->defaultSort('name') ->defaultSort('name')
->columns([ ->columns([
TextColumn::make('name') TextColumn::make('name')
->label(trans('server/file.name'))
->searchable() ->searchable()
->sortable() ->sortable()
->icon(fn (File $file) => $file->getIcon()), ->icon(fn (File $file) => $file->getIcon()),
BytesColumn::make('size') BytesColumn::make('size')
->label(trans('server/file.size'))
->visibleFrom('md') ->visibleFrom('md')
->state(fn (File $file) => $file->is_directory ? null : $file->size) ->state(fn (File $file) => $file->is_directory ? null : $file->size)
->sortable(), ->sortable(),
DateTimeColumn::make('modified_at') DateTimeColumn::make('modified_at')
->label(trans('server/file.modified_at'))
->visibleFrom('md') ->visibleFrom('md')
->since() ->since()
->sortable(), ->sortable(),
@ -116,7 +119,7 @@ class ListFiles extends ListRecords
->actions([ ->actions([
Action::make('view') Action::make('view')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server))
->label('Open') ->label(trans('server/file.actions.open'))
->icon('tabler-eye') ->icon('tabler-eye')
->visible(fn (File $file) => $file->is_directory) ->visible(fn (File $file) => $file->is_directory)
->url(fn (File $file) => self::getUrl(['path' => join_paths($this->path, $file->name)])), ->url(fn (File $file) => self::getUrl(['path' => join_paths($this->path, $file->name)])),
@ -128,11 +131,11 @@ class ListFiles extends ListRecords
ActionGroup::make([ ActionGroup::make([
Action::make('rename') Action::make('rename')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
->label('Rename') ->label(trans('server/file.actions.rename.title'))
->icon('tabler-forms') ->icon('tabler-forms')
->form([ ->form([
TextInput::make('name') TextInput::make('name')
->label('File name') ->label(trans('server/file.actions.rename.name'))
->default(fn (File $file) => $file->name) ->default(fn (File $file) => $file->name)
->required(), ->required(),
]) ])
@ -149,14 +152,14 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title('File Renamed') ->title(trans('server/file.actions.rename.notification'))
->body(fn () => $file->name . ' -> ' . $data['name']) ->body(fn () => $file->name . ' -> ' . $data['name'])
->success() ->success()
->send(); ->send();
}), }),
Action::make('copy') Action::make('copy')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
->label('Copy') ->label(trans('server/file.actions.copy.title'))
->icon('tabler-copy') ->icon('tabler-copy')
->visible(fn (File $file) => $file->is_file) ->visible(fn (File $file) => $file->is_file)
->action(function (File $file) { ->action(function (File $file) {
@ -167,7 +170,7 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title('File copied') ->title(trans('server/file.actions.copy.notification'))
->success() ->success()
->send(); ->send();
@ -175,18 +178,18 @@ class ListFiles extends ListRecords
}), }),
Action::make('download') Action::make('download')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server))
->label('Download') ->label(trans('server/file.actions.download'))
->icon('tabler-download') ->icon('tabler-download')
->visible(fn (File $file) => $file->is_file) ->visible(fn (File $file) => $file->is_file)
->url(fn (File $file) => DownloadFiles::getUrl(['path' => join_paths($this->path, $file->name)]), true), ->url(fn (File $file) => DownloadFiles::getUrl(['path' => join_paths($this->path, $file->name)]), true),
Action::make('move') Action::make('move')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
->label('Move') ->label(trans('server/file.actions.move.title'))
->icon('tabler-replace') ->icon('tabler-replace')
->form([ ->form([
TextInput::make('location') TextInput::make('location')
->label('New location') ->label(trans('server/file.actions.move.new_location'))
->hint('Enter the location of this file or folder, relative to the current directory.') ->hint(trans('server/file.actions.move.new_location_hint'))
->required() ->required()
->live(), ->live(),
Placeholder::make('new_location') Placeholder::make('new_location')
@ -209,22 +212,24 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title('File Moved') ->title(trans('server/file.actions.move.notification'))
->body($oldLocation . ' -> ' . $newLocation) ->body($oldLocation . ' -> ' . $newLocation)
->success() ->success()
->send(); ->send();
}), }),
Action::make('permissions') Action::make('permissions')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
->label('Permissions') ->label(trans('server/file.actions.permissions.title'))
->icon('tabler-license') ->icon('tabler-license')
->form([ ->form([
CheckboxList::make('owner') CheckboxList::make('owner')
->label(trans('server/file.actions.permissions.owner'))
->bulkToggleable() ->bulkToggleable()
->columns(3)
->options([ ->options([
'read' => 'Read', 'read' => trans('server/file.actions.permissions.read'),
'write' => 'Write', 'write' => trans('server/file.actions.permissions.write'),
'execute' => 'Execute', 'execute' => trans('server/file.actions.permissions.execute'),
]) ])
->formatStateUsing(function ($state, File $file) { ->formatStateUsing(function ($state, File $file) {
$mode = (int) substr((string) $file->mode_bits, 0, 1); $mode = (int) substr((string) $file->mode_bits, 0, 1);
@ -232,11 +237,13 @@ class ListFiles extends ListRecords
return $this->getPermissionsFromModeBit($mode); return $this->getPermissionsFromModeBit($mode);
}), }),
CheckboxList::make('group') CheckboxList::make('group')
->label(trans('server/file.actions.permissions.group'))
->bulkToggleable() ->bulkToggleable()
->columns(3)
->options([ ->options([
'read' => 'Read', 'read' => trans('server/file.actions.permissions.read'),
'write' => 'Write', 'write' => trans('server/file.actions.permissions.write'),
'execute' => 'Execute', 'execute' => trans('server/file.actions.permissions.execute'),
]) ])
->formatStateUsing(function ($state, File $file) { ->formatStateUsing(function ($state, File $file) {
$mode = (int) substr((string) $file->mode_bits, 1, 1); $mode = (int) substr((string) $file->mode_bits, 1, 1);
@ -244,11 +251,13 @@ class ListFiles extends ListRecords
return $this->getPermissionsFromModeBit($mode); return $this->getPermissionsFromModeBit($mode);
}), }),
CheckboxList::make('public') CheckboxList::make('public')
->label(trans('server/file.actions.permissions.public'))
->bulkToggleable() ->bulkToggleable()
->columns(3)
->options([ ->options([
'read' => 'Read', 'read' => trans('server/file.actions.permissions.read'),
'write' => 'Write', 'write' => trans('server/file.actions.permissions.write'),
'execute' => 'Execute', 'execute' => trans('server/file.actions.permissions.execute'),
]) ])
->formatStateUsing(function ($state, File $file) { ->formatStateUsing(function ($state, File $file) {
$mode = (int) substr((string) $file->mode_bits, 2, 1); $mode = (int) substr((string) $file->mode_bits, 2, 1);
@ -266,17 +275,17 @@ class ListFiles extends ListRecords
$this->getDaemonFileRepository()->chmodFiles($this->path, [['file' => $file->name, 'mode' => $mode]]); $this->getDaemonFileRepository()->chmodFiles($this->path, [['file' => $file->name, 'mode' => $mode]]);
Notification::make() Notification::make()
->title('Permissions changed to ' . $mode) ->title(trans('server/file.actions.permissions.notification', ['mode' => $mode]))
->success() ->success()
->send(); ->send();
}), }),
Action::make('archive') Action::make('archive')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server))
->label('Archive') ->label(trans('server/file.actions.archive.title'))
->icon('tabler-archive') ->icon('tabler-archive')
->form([ ->form([
TextInput::make('name') TextInput::make('name')
->label('Archive name') ->label(trans('server/file.actions.archive.archive_name'))
->placeholder(fn () => 'archive-' . str(Carbon::now()->toRfc3339String())->replace(':', '')->before('+0000') . 'Z') ->placeholder(fn () => 'archive-' . str(Carbon::now()->toRfc3339String())->replace(':', '')->before('+0000') . 'Z')
->suffix('.tar.gz'), ->suffix('.tar.gz'),
]) ])
@ -290,7 +299,7 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title('Archive created') ->title(trans('server/file.actions.archive.notification'))
->body($archive['name']) ->body($archive['name'])
->success() ->success()
->send(); ->send();
@ -299,7 +308,7 @@ class ListFiles extends ListRecords
}), }),
Action::make('unarchive') Action::make('unarchive')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server))
->label('Unarchive') ->label(trans('server/file.actions.unarchive.title'))
->icon('tabler-archive') ->icon('tabler-archive')
->visible(fn (File $file) => $file->isArchive()) ->visible(fn (File $file) => $file->isArchive())
->action(function (File $file) { ->action(function (File $file) {
@ -311,7 +320,7 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title('Unarchive completed') ->title(trans('server/file.actions.unarchive.notification'))
->success() ->success()
->send(); ->send();
@ -339,8 +348,8 @@ class ListFiles extends ListRecords
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server))
->form([ ->form([
TextInput::make('location') TextInput::make('location')
->label('Directory') ->label(trans('server/file.actions.move.directory'))
->hint('Enter the new directory, relative to the current directory.') ->hint(trans('server/file.actions.move.directory_hint'))
->required() ->required()
->live(), ->live(),
Placeholder::make('new_location') Placeholder::make('new_location')
@ -358,7 +367,7 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title(count($files) . ' Files were moved to ' . resolve_path(join_paths($this->path, $location))) ->title(trans('server/file.actions.move.bulk_notification', ['count' => count($files), 'directory' => resolve_path(join_paths($this->path, $location))]))
->success() ->success()
->send(); ->send();
}), }),
@ -366,7 +375,7 @@ class ListFiles extends ListRecords
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server))
->form([ ->form([
TextInput::make('name') TextInput::make('name')
->label('Archive name') ->label(trans('server/file.actions.archive.archive_name'))
->placeholder(fn () => 'archive-' . str(Carbon::now()->toRfc3339String())->replace(':', '')->before('+0000') . 'Z') ->placeholder(fn () => 'archive-' . str(Carbon::now()->toRfc3339String())->replace(':', '')->before('+0000') . 'Z')
->suffix('.tar.gz'), ->suffix('.tar.gz'),
]) ])
@ -382,7 +391,7 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title('Archive created') ->title(trans('server/file.actions.archive.notification'))
->body($archive['name']) ->body($archive['name'])
->success() ->success()
->send(); ->send();
@ -401,7 +410,7 @@ class ListFiles extends ListRecords
->log(); ->log();
Notification::make() Notification::make()
->title(count($files) . ' Files deleted.') ->title(trans('server/file.actions.delete.bulk_notification', ['count' => count($files)]))
->success() ->success()
->send(); ->send();
}), }),
@ -417,10 +426,10 @@ class ListFiles extends ListRecords
return [ return [
HeaderAction::make('new_file') HeaderAction::make('new_file')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
->tooltip('New File') ->tooltip(trans('server/file.actions.new_file.title'))
->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->icon('tabler-file-plus')->iconButton()->iconSize(IconSize::Large)
->color('primary') ->color('primary')
->modalSubmitActionLabel('Create') ->modalSubmitActionLabel(trans('server/file.actions.new_file.create'))
->action(function ($data) { ->action(function ($data) {
$path = join_paths($this->path, $data['name']); $path = join_paths($this->path, $data['name']);
try { try {
@ -441,10 +450,10 @@ class ListFiles extends ListRecords
}) })
->form([ ->form([
TextInput::make('name') TextInput::make('name')
->label('File Name') ->label(trans('server/file.actions.new_file.file_name'))
->required(), ->required(),
Select::make('lang') Select::make('lang')
->label('Syntax Highlighting') ->label(trans('server/file.actions.new_file.syntax'))
->searchable() ->searchable()
->native(false) ->native(false)
->live() ->live()
@ -460,7 +469,7 @@ class ListFiles extends ListRecords
HeaderAction::make('new_folder') HeaderAction::make('new_folder')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->icon('tabler-folder-plus')->iconButton()->iconSize(IconSize::Large)
->tooltip('New Folder') ->tooltip(trans('server/file.actions.new_folder.title'))
->color('primary') ->color('primary')
->action(function ($data) { ->action(function ($data) {
try { try {
@ -482,13 +491,13 @@ class ListFiles extends ListRecords
}) })
->form([ ->form([
TextInput::make('name') TextInput::make('name')
->label('Folder Name') ->label(trans('server/file.actions.new_folder.folder_name'))
->required(), ->required(),
]), ]),
HeaderAction::make('upload') HeaderAction::make('upload')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server))
->hiddenLabel()->icon('tabler-upload')->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->icon('tabler-upload')->iconButton()->iconSize(IconSize::Large)
->tooltip('Upload') ->tooltip(trans('server/file.actions.upload.title'))
->color('success') ->color('success')
->action(function ($data) { ->action(function ($data) {
if (count($data['files']) > 0 && !isset($data['url'])) { if (count($data['files']) > 0 && !isset($data['url'])) {
@ -516,7 +525,7 @@ class ListFiles extends ListRecords
Tabs::make() Tabs::make()
->contained(false) ->contained(false)
->schema([ ->schema([
Tab::make('Upload Files') Tab::make(trans('server/file.actions.upload.from_files'))
->live() ->live()
->schema([ ->schema([
FileUpload::make('files') FileUpload::make('files')
@ -526,12 +535,12 @@ class ListFiles extends ListRecords
->maxSize((int) round($server->node->upload_size * (config('panel.use_binary_prefix') ? 1.048576 * 1024 : 1000))) ->maxSize((int) round($server->node->upload_size * (config('panel.use_binary_prefix') ? 1.048576 * 1024 : 1000)))
->multiple(), ->multiple(),
]), ]),
Tab::make('Upload From URL') Tab::make(trans('server/file.actions.upload.url'))
->live() ->live()
->disabled(fn (Get $get) => count($get('files')) > 0) ->disabled(fn (Get $get) => count($get('files')) > 0)
->schema([ ->schema([
TextInput::make('url') TextInput::make('url')
->label('URL') ->label(trans('server/file.actions.upload.url'))
->url(), ->url(),
]), ]),
]), ]),
@ -539,14 +548,15 @@ class ListFiles extends ListRecords
HeaderAction::make('search') HeaderAction::make('search')
->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server))
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->tooltip('Global Search') ->tooltip(trans('server/file.actions.global_search.title'))
->color('primary') ->color('primary')
->icon('tabler-world-search') ->icon('tabler-world-search')
->modalHeading('Global Search') ->modalHeading(trans('server/file.actions.global_search.title'))
->modalSubmitActionLabel('Search') ->modalSubmitActionLabel(trans('server/file.actions.global_search.search'))
->form([ ->form([
TextInput::make('searchTerm') TextInput::make('searchTerm')
->placeholder('Enter a search term, e.g. *.txt') ->label(trans('server/file.actions.global_search.search_term'))
->placeholder(trans('server/file.actions.global_search.search_term_placeholder'))
->required() ->required()
->regex('/^[^*]*\*?[^*]*$/') ->regex('/^[^*]*\*?[^*]*$/')
->minValue(3), ->minValue(3),
@ -594,4 +604,9 @@ class ListFiles extends ListRecords
->where('path', '.*'), ->where('path', '.*'),
); );
} }
public function getTitle(): string
{
return trans('server/file.title');
}
} }

View File

@ -13,6 +13,7 @@ use Filament\Facades\Filament;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
use Filament\Tables\Columns\TextColumn; use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Contracts\Support\Htmlable;
use Livewire\Attributes\Locked; use Livewire\Attributes\Locked;
use Livewire\Attributes\Url; use Livewire\Attributes\Url;
@ -23,8 +24,6 @@ class SearchFiles extends ListRecords
protected static string $resource = FileResource::class; protected static string $resource = FileResource::class;
protected static ?string $title = 'Global Search';
#[Locked] #[Locked]
public string $searchTerm; public string $searchTerm;
@ -37,7 +36,7 @@ class SearchFiles extends ListRecords
return [ return [
$resource::getUrl() => $resource::getBreadcrumb(), $resource::getUrl() => $resource::getBreadcrumb(),
self::getUrl(['searchTerm' => $this->searchTerm]) => 'Search "' . $this->searchTerm . '"', self::getUrl(['searchTerm' => $this->searchTerm]) => trans('server/file.actions.global_search.search') . ' "' . $this->searchTerm . '"',
]; ];
} }
@ -51,10 +50,18 @@ class SearchFiles extends ListRecords
->query(fn () => File::get($server, $this->path, $this->searchTerm)->orderByDesc('is_directory')->orderBy('name')) ->query(fn () => File::get($server, $this->path, $this->searchTerm)->orderByDesc('is_directory')->orderBy('name'))
->columns([ ->columns([
TextColumn::make('name') TextColumn::make('name')
->label(trans('server/file.name'))
->searchable() ->searchable()
->sortable()
->icon(fn (File $file) => $file->getIcon()), ->icon(fn (File $file) => $file->getIcon()),
BytesColumn::make('size'), BytesColumn::make('size')
->label(trans('server/file.size'))
->visibleFrom('md')
->state(fn (File $file) => $file->size)
->sortable(),
DateTimeColumn::make('modified_at') DateTimeColumn::make('modified_at')
->label(trans('server/file.modified_at'))
->visibleFrom('md')
->since() ->since()
->sortable(), ->sortable(),
]) ])
@ -66,4 +73,9 @@ class SearchFiles extends ListRecords
return $file->canEdit() ? EditFiles::getUrl(['path' => join_paths($this->path, $file->name)]) : null; return $file->canEdit() ? EditFiles::getUrl(['path' => join_paths($this->path, $file->name)]) : null;
}); });
} }
public function getTitle(): string|Htmlable
{
return trans('server/file.actions.global_search.title');
}
} }

View File

@ -85,21 +85,20 @@ class ScheduleResource extends Resource
]) ])
->schema([ ->schema([
TextInput::make('name') TextInput::make('name')
->label(trans('server/schedule.name'))
->columnSpanFull() ->columnSpanFull()
->label('Schedule Name')
->placeholder('A human readable identifier for this schedule.')
->autocomplete(false) ->autocomplete(false)
->required(), ->required(),
Toggle::make('only_when_online') Toggle::make('only_when_online')
->label('Only when Server is Online?') ->label(trans('server/schedule.only_online'))
->hintIconTooltip('Only execute this schedule when the server is in a running state.') ->hintIconTooltip(trans('server/schedule.only_online_hint'))
->hintIcon('tabler-question-mark') ->hintIcon('tabler-question-mark')
->inline(false) ->inline(false)
->required() ->required()
->default(1), ->default(1),
Toggle::make('is_active') Toggle::make('is_active')
->label('Enable Schedule?') ->label(trans('server/schedule.enabled'))
->hintIconTooltip('This schedule will be executed automatically if enabled.') ->hintIconTooltip(trans('server/schedule.enabled_hint'))
->hintIcon('tabler-question-mark') ->hintIcon('tabler-question-mark')
->inline(false) ->inline(false)
->hiddenOn('view') ->hiddenOn('view')
@ -107,7 +106,7 @@ class ScheduleResource extends Resource
->default(1), ->default(1),
ToggleButtons::make('Status') ToggleButtons::make('Status')
->formatStateUsing(fn (Schedule $schedule) => !$schedule->is_active ? 'inactive' : ($schedule->is_processing ? 'processing' : 'active')) ->formatStateUsing(fn (Schedule $schedule) => !$schedule->is_active ? 'inactive' : ($schedule->is_processing ? 'processing' : 'active'))
->options(fn (Schedule $schedule) => !$schedule->is_active ? ['inactive' => 'Inactive'] : ($schedule->is_processing ? ['processing' => 'Processing'] : ['active' => 'Active'])) ->options(fn (Schedule $schedule) => !$schedule->is_active ? ['inactive' => trans('server/schedule.inactive')] : ($schedule->is_processing ? ['processing' => trans('server/schedule.processing')] : ['active' => trans('server/schedule.active')]))
->colors([ ->colors([
'inactive' => 'danger', 'inactive' => 'danger',
'processing' => 'warning', 'processing' => 'warning',
@ -115,22 +114,27 @@ class ScheduleResource extends Resource
]) ])
->visibleOn('view'), ->visibleOn('view'),
Section::make('Cron') Section::make('Cron')
->description(fn (Get $get) => new HtmlString('Please keep in mind that the cron inputs below always assume UTC.<br>Next run in your timezone (' . auth()->user()->timezone . '): <b>'. Utilities::getScheduleNextRunDate($get('cron_minute'), $get('cron_hour'), $get('cron_day_of_month'), $get('cron_month'), $get('cron_day_of_week'))->timezone(auth()->user()->timezone) . '</b>')) ->label(trans('server/schedule.cron'))
->description(fn (Get $get) => new HtmlString(trans('server/schedule.cron_body') . '<br>' . trans('server/schedule.cron_timezone', ['timezone' => auth()->user()->timezone, 'next_run' => Utilities::getScheduleNextRunDate($get('cron_minute'), $get('cron_hour'), $get('cron_day_of_month'), $get('cron_month'), $get('cron_day_of_week'))->timezone(auth()->user()->timezone)])))
->schema([ ->schema([
Actions::make([ Actions::make([
CronPresetAction::make('hourly') CronPresetAction::make('hourly')
->label(trans('server/schedule.time.hourly'))
->cron('0', '*', '*', '*', '*'), ->cron('0', '*', '*', '*', '*'),
CronPresetAction::make('daily') CronPresetAction::make('daily')
->label(trans('server/schedule.time.daily'))
->cron('0', '0', '*', '*', '*'), ->cron('0', '0', '*', '*', '*'),
CronPresetAction::make('weekly_monday') CronPresetAction::make('weekly_monday')
->label('Weekly (Monday)') ->label(trans('server/schedule.time.weekly_mon'))
->cron('0', '0', '*', '*', '1'), ->cron('0', '0', '*', '*', '1'),
CronPresetAction::make('weekly_sunday') CronPresetAction::make('weekly_sunday')
->label('Weekly (Sunday)') ->label(trans('server/schedule.time.weekly_sun'))
->cron('0', '0', '*', '*', '0'), ->cron('0', '0', '*', '*', '0'),
CronPresetAction::make('monthly') CronPresetAction::make('monthly')
->label(trans('server/schedule.time.monthly'))
->cron('0', '0', '1', '*', '*'), ->cron('0', '0', '1', '*', '*'),
CronPresetAction::make('every_x_minutes') CronPresetAction::make('every_x_minutes')
->label(trans('server/schedule.time.every_min'))
->color(fn (Get $get) => str($get('cron_minute'))->startsWith('*/') ->color(fn (Get $get) => str($get('cron_minute'))->startsWith('*/')
&& $get('cron_hour') == '*' && $get('cron_hour') == '*'
&& $get('cron_day_of_month') == '*' && $get('cron_day_of_month') == '*'
@ -142,8 +146,8 @@ class ScheduleResource extends Resource
->numeric() ->numeric()
->minValue(1) ->minValue(1)
->maxValue(60) ->maxValue(60)
->prefix('Every') ->prefix(trans('server/schedule.time.every'))
->suffix('Minutes'), ->suffix(trans('server/schedule.time.minutes')),
]) ])
->action(function (Set $set, $data) { ->action(function (Set $set, $data) {
$set('cron_minute', '*/' . $data['x']); $set('cron_minute', '*/' . $data['x']);
@ -164,8 +168,8 @@ class ScheduleResource extends Resource
->numeric() ->numeric()
->minValue(1) ->minValue(1)
->maxValue(24) ->maxValue(24)
->prefix('Every') ->prefix(trans('server/schedule.time.every'))
->suffix('Hours'), ->suffix(trans('server/schedule.time.hours')),
]) ])
->action(function (Set $set, $data) { ->action(function (Set $set, $data) {
$set('cron_minute', '0'); $set('cron_minute', '0');
@ -186,8 +190,8 @@ class ScheduleResource extends Resource
->numeric() ->numeric()
->minValue(1) ->minValue(1)
->maxValue(24) ->maxValue(24)
->prefix('Every') ->prefix(trans('server/schedule.time.every'))
->suffix('Days'), ->suffix(trans('server/schedule.time.days')),
]) ])
->action(function (Set $set, $data) { ->action(function (Set $set, $data) {
$set('cron_minute', '0'); $set('cron_minute', '0');
@ -208,8 +212,8 @@ class ScheduleResource extends Resource
->numeric() ->numeric()
->minValue(1) ->minValue(1)
->maxValue(24) ->maxValue(24)
->prefix('Every') ->prefix(trans('server/schedule.time.every'))
->suffix('Months'), ->suffix(trans('server/schedule.time.months')),
]) ])
->action(function (Set $set, $data) { ->action(function (Set $set, $data) {
$set('cron_minute', '0'); $set('cron_minute', '0');
@ -227,15 +231,15 @@ class ScheduleResource extends Resource
->form([ ->form([
Select::make('x') Select::make('x')
->label('') ->label('')
->prefix('Every') ->prefix(trans('server/schedule.time.every'))
->options([ ->options([
'1' => 'Monday', '1' => trans('server/schedule.time.monday'),
'2' => 'Tuesday', '2' => trans('server/schedule.time.tuesday'),
'3' => 'Wednesday', '3' => trans('server/schedule.time.wednesday'),
'4' => 'Thursday', '4' => trans('server/schedule.time.thursday'),
'5' => 'Friday', '5' => trans('server/schedule.time.friday'),
'6' => 'Saturday', '6' => trans('server/schedule.time.saturday'),
'0' => 'Sunday', '0' => trans('server/schedule.time.sunday'),
]) ])
->selectablePlaceholder(false) ->selectablePlaceholder(false)
->native(false), ->native(false),
@ -251,47 +255,47 @@ class ScheduleResource extends Resource
->hiddenOn('view'), ->hiddenOn('view'),
Group::make([ Group::make([
TextInput::make('cron_minute') TextInput::make('cron_minute')
->label(trans('server/schedule.time.minute'))
->columnSpan([ ->columnSpan([
'default' => 2, 'default' => 2,
'lg' => 1, 'lg' => 1,
]) ])
->label('Minute')
->default('*/5') ->default('*/5')
->required() ->required()
->live(), ->live(),
TextInput::make('cron_hour') TextInput::make('cron_hour')
->label(trans('server/schedule.time.hour'))
->columnSpan([ ->columnSpan([
'default' => 2, 'default' => 2,
'lg' => 1, 'lg' => 1,
]) ])
->label('Hour')
->default('*') ->default('*')
->required() ->required()
->live(), ->live(),
TextInput::make('cron_day_of_month') TextInput::make('cron_day_of_month')
->label(trans('server/schedule.time.day_of_month'))
->columnSpan([ ->columnSpan([
'default' => 2, 'default' => 2,
'lg' => 1, 'lg' => 1,
]) ])
->label('Day of Month')
->default('*') ->default('*')
->required() ->required()
->live(), ->live(),
TextInput::make('cron_month') TextInput::make('cron_month')
->label(trans('server/schedule.time.month'))
->columnSpan([ ->columnSpan([
'default' => 2, 'default' => 2,
'lg' => 1, 'lg' => 1,
]) ])
->label('Month')
->default('*') ->default('*')
->required() ->required()
->live(), ->live(),
TextInput::make('cron_day_of_week') TextInput::make('cron_day_of_week')
->label(trans('server/schedule.time.day_of_week'))
->columnSpan([ ->columnSpan([
'default' => 2, 'default' => 2,
'lg' => 1, 'lg' => 1,
]) ])
->label('Day of Week')
->default('*') ->default('*')
->required() ->required()
->live(), ->live(),
@ -309,22 +313,26 @@ class ScheduleResource extends Resource
return $table return $table
->columns([ ->columns([
TextColumn::make('name') TextColumn::make('name')
->label(trans('server/schedule.name'))
->searchable(), ->searchable(),
TextColumn::make('cron') TextColumn::make('cron')
->label(trans('server/schedule.cron'))
->state(fn (Schedule $schedule) => $schedule->cron_minute . ' ' . $schedule->cron_hour . ' ' . $schedule->cron_day_of_month . ' ' . $schedule->cron_month . ' ' . $schedule->cron_day_of_week), ->state(fn (Schedule $schedule) => $schedule->cron_minute . ' ' . $schedule->cron_hour . ' ' . $schedule->cron_day_of_month . ' ' . $schedule->cron_month . ' ' . $schedule->cron_day_of_week),
TextColumn::make('status') TextColumn::make('status')
->state(fn (Schedule $schedule) => !$schedule->is_active ? 'Inactive' : ($schedule->is_processing ? 'Processing' : 'Active')), ->label(trans('server/schedule.status'))
->state(fn (Schedule $schedule) => !$schedule->is_active ? trans('server/schedule.inactive') : ($schedule->is_processing ? trans('server/schedule.processing') : trans('server/schedule.active'))),
IconColumn::make('only_when_online') IconColumn::make('only_when_online')
->label(trans('server/schedule.online_only'))
->boolean() ->boolean()
->sortable(), ->sortable(),
DateTimeColumn::make('last_run_at') DateTimeColumn::make('last_run_at')
->label('Last run') ->label(trans('server/schedule.last_run'))
->placeholder('Never') ->placeholder(trans('server/schedule.never'))
->since() ->since()
->sortable(), ->sortable(),
DateTimeColumn::make('next_run_at') DateTimeColumn::make('next_run_at')
->label('Next run') ->label(trans('server/schedule.next_run'))
->placeholder('Never') ->placeholder(trans('server/schedule.never'))
->since() ->since()
->sortable() ->sortable()
->state(fn (Schedule $schedule) => $schedule->is_active ? $schedule->next_run_at : null), ->state(fn (Schedule $schedule) => $schedule->is_active ? $schedule->next_run_at : null),
@ -367,11 +375,16 @@ class ScheduleResource extends Resource
return Utilities::getScheduleNextRunDate($minute, $hour, $dayOfMonth, $month, $dayOfWeek); return Utilities::getScheduleNextRunDate($minute, $hour, $dayOfMonth, $month, $dayOfWeek);
} catch (Exception) { } catch (Exception) {
Notification::make() Notification::make()
->title('The cron data provided does not evaluate to a valid expression') ->title(trans('server/schedule.notification_invalid_cron'))
->danger() ->danger()
->send(); ->send();
throw new Halt(); throw new Halt();
} }
} }
public static function getNavigationLabel(): string
{
return trans('server/schedule.title');
}
} }

View File

@ -49,7 +49,7 @@ class EditSchedule extends EditRecord
Actions\DeleteAction::make() Actions\DeleteAction::make()
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-trash') ->icon('tabler-trash')
->tooltip('Delete Schedule') ->tooltip(trans('server/schedule.delete'))
->after(function ($record) { ->after(function ($record) {
Activity::event('server:schedule.delete') Activity::event('server:schedule.delete')
->property('name', $record->name) ->property('name', $record->name)
@ -58,15 +58,15 @@ class EditSchedule extends EditRecord
ExportScheduleAction::make() ExportScheduleAction::make()
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-download') ->icon('tabler-download')
->tooltip('Export Schedule'), ->tooltip(trans('server/schedule.export')),
$this->getSaveFormAction()->formId('form') $this->getSaveFormAction()->formId('form')
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-device-floppy') ->icon('tabler-device-floppy')
->tooltip('Save Schedule'), ->tooltip(trans('server/schedule.save')),
$this->getCancelFormAction()->formId('form') $this->getCancelFormAction()->formId('form')
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-cancel') ->icon('tabler-cancel')
->tooltip('Cancel'), ->tooltip(trans('server/schedule.cancel')),
]; ];
} }

View File

@ -26,11 +26,11 @@ class ListSchedules extends ListRecords
CreateAction::make() CreateAction::make()
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-calendar-plus') ->icon('tabler-calendar-plus')
->tooltip('New Schedule'), ->tooltip(trans('server/schedule.new')),
ImportScheduleAction::make() ImportScheduleAction::make()
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-download') ->icon('tabler-download')
->tooltip('Import Schedule'), ->tooltip(trans('server/schedule.import')),
]; ];
} }
@ -38,4 +38,9 @@ class ListSchedules extends ListRecords
{ {
return []; return [];
} }
public function getTitle(): string
{
return trans('server/schedule.title');
}
} }

View File

@ -29,7 +29,7 @@ class ViewSchedule extends ViewRecord
return [ return [
Action::make('runNow') Action::make('runNow')
->authorize(fn () => auth()->user()->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant())) ->authorize(fn () => auth()->user()->can(Permission::ACTION_SCHEDULE_UPDATE, Filament::getTenant()))
->label(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? 'No tasks' : ($schedule->is_processing ? 'Processing' : 'Run now')) ->label(fn (Schedule $schedule) => $schedule->tasks->count() === 0 ? trans('server/schedule.no_tasks') : ($schedule->is_processing ? trans('server/schedule.processing') : trans('server/schedule.run_now')))
->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->is_processing ? 'warning' : 'primary') ->color(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->is_processing ? 'warning' : 'primary')
->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->is_processing) ->disabled(fn (Schedule $schedule) => $schedule->tasks->count() === 0 || $schedule->is_processing)
->action(function (ProcessScheduleService $service, Schedule $schedule) { ->action(function (ProcessScheduleService $service, Schedule $schedule) {
@ -45,7 +45,7 @@ class ViewSchedule extends ViewRecord
EditAction::make() EditAction::make()
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-calendar-code') ->icon('tabler-calendar-code')
->tooltip('Edit Schedule'), ->tooltip(trans('server/schedule.edit')),
]; ];
} }

View File

@ -30,10 +30,10 @@ class TasksRelationManager extends RelationManager
private function getActionOptions(bool $full = true): array private function getActionOptions(bool $full = true): array
{ {
return [ return [
Task::ACTION_POWER => $full ? 'Send power action' : 'Power action', Task::ACTION_POWER => $full ? trans('server/schedule.tasks.actions.power.title') : trans('server/schedule.tasks.actions.power.action'),
Task::ACTION_COMMAND => $full ? 'Send command' : 'Command', Task::ACTION_COMMAND => $full ? trans('server/schedule.tasks.actions.command.title') : trans('server/schedule.tasks.actions.command.command'),
Task::ACTION_BACKUP => $full ? 'Create backup' : 'Files to ignore', Task::ACTION_BACKUP => $full ? trans('server/schedule.tasks.actions.backup.title') : trans('server/schedule.tasks.actions.backup.files_to_ignore'),
Task::ACTION_DELETE_FILES => $full ? 'Delete files' : 'Files to delete', Task::ACTION_DELETE_FILES => $full ? trans('server/schedule.tasks.actions.delete.title') : trans('server/schedule.tasks.actions.delete.files_to_delete'),
]; ];
} }
@ -44,6 +44,7 @@ class TasksRelationManager extends RelationManager
{ {
return [ return [
Select::make('action') Select::make('action')
->label(trans('server/schedule.tasks.actions.title'))
->required() ->required()
->live() ->live()
->disableOptionWhen(fn (string $value) => $value === Task::ACTION_BACKUP && $schedule->server->backup_limit === 0) ->disableOptionWhen(fn (string $value) => $value === Task::ACTION_BACKUP && $schedule->server->backup_limit === 0)
@ -53,27 +54,29 @@ class TasksRelationManager extends RelationManager
->afterStateUpdated(fn ($state, Set $set) => $set('payload', $state === Task::ACTION_POWER ? 'restart' : null)), ->afterStateUpdated(fn ($state, Set $set) => $set('payload', $state === Task::ACTION_POWER ? 'restart' : null)),
Textarea::make('payload') Textarea::make('payload')
->hidden(fn (Get $get) => $get('action') === Task::ACTION_POWER) ->hidden(fn (Get $get) => $get('action') === Task::ACTION_POWER)
->label(fn (Get $get) => $this->getActionOptions(false)[$get('action')] ?? 'Payload'), ->label(fn (Get $get) => $this->getActionOptions(false)[$get('action')] ?? trans('server/schedule.tasks.payload')),
Select::make('payload') Select::make('payload')
->visible(fn (Get $get) => $get('action') === Task::ACTION_POWER) ->visible(fn (Get $get) => $get('action') === Task::ACTION_POWER)
->label('Power Action') ->label(trans('server/schedule.tasks.actions.power.action'))
->required() ->required()
->options([ ->options([
'start' => 'Start', 'start' => trans('server/schedule.tasks.actions.power.start'),
'restart' => 'Restart', 'restart' => trans('server/schedule.tasks.actions.power.restart'),
'stop' => 'Stop', 'stop' => trans('server/schedule.tasks.actions.power.stop'),
'kill' => 'Kill', 'kill' => trans('server/schedule.tasks.actions.power.kill'),
]) ])
->selectablePlaceholder(false) ->selectablePlaceholder(false)
->default('restart'), ->default('restart'),
TextInput::make('time_offset') TextInput::make('time_offset')
->label(trans('server/schedule.tasks.time_offset'))
->hidden(fn (Get $get) => config('queue.default') === 'sync' || $get('sequence_id') === 1) ->hidden(fn (Get $get) => config('queue.default') === 'sync' || $get('sequence_id') === 1)
->default(0) ->default(0)
->numeric() ->numeric()
->minValue(0) ->minValue(0)
->maxValue(900) ->maxValue(900)
->suffix('Seconds'), ->suffix(trans('server/schedule.tasks.seconds')),
Toggle::make('continue_on_failure'), Toggle::make('continue_on_failure')
->label(trans('server/schedule.tasks.continue_on_failure')),
]; ];
} }
@ -87,17 +90,21 @@ class TasksRelationManager extends RelationManager
->defaultSort('sequence_id') ->defaultSort('sequence_id')
->columns([ ->columns([
TextColumn::make('action') TextColumn::make('action')
->label(trans('server/schedule.tasks.actions.title'))
->state(fn (Task $task) => $this->getActionOptions()[$task->action] ?? $task->action), ->state(fn (Task $task) => $this->getActionOptions()[$task->action] ?? $task->action),
TextColumn::make('payload') TextColumn::make('payload')
->label(trans('server/schedule.tasks.payload'))
->state(fn (Task $task) => match ($task->payload) { ->state(fn (Task $task) => match ($task->payload) {
'start', 'restart', 'stop', 'kill' => mb_ucfirst($task->payload), 'start', 'restart', 'stop', 'kill' => mb_ucfirst($task->payload),
default => explode(PHP_EOL, $task->payload) default => explode(PHP_EOL, $task->payload)
}) })
->badge(), ->badge(),
TextColumn::make('time_offset') TextColumn::make('time_offset')
->label(trans('server/schedule.tasks.time_offset'))
->hidden(fn () => config('queue.default') === 'sync') ->hidden(fn () => config('queue.default') === 'sync')
->suffix(' Seconds'), ->suffix(' '. trans('server/schedule.tasks.seconds')),
IconColumn::make('continue_on_failure') IconColumn::make('continue_on_failure')
->label(trans('server/schedule.tasks.continue_on_failure'))
->boolean(), ->boolean(),
]) ])
->actions([ ->actions([
@ -133,7 +140,7 @@ class TasksRelationManager extends RelationManager
->headerActions([ ->headerActions([
CreateAction::make() CreateAction::make()
->createAnother(false) ->createAnother(false)
->label(fn () => $schedule->tasks()->count() >= config('panel.client_features.schedules.per_schedule_task_limit', 10) ? 'Task Limit Reached' : 'Create Task') ->label(fn () => $schedule->tasks()->count() >= config('panel.client_features.schedules.per_schedule_task_limit', 10) ? trans('server/schedule.tasks.limit') : trans('server/schedule.tasks.create'))
->disabled(fn () => $schedule->tasks()->count() >= config('panel.client_features.schedules.per_schedule_task_limit', 10)) ->disabled(fn () => $schedule->tasks()->count() >= config('panel.client_features.schedules.per_schedule_task_limit', 10))
->form($this->getTaskForm($schedule)) ->form($this->getTaskForm($schedule))
->action(function ($data) use ($schedule) { ->action(function ($data) use ($schedule) {

View File

@ -91,14 +91,14 @@ class UserResource extends Resource
foreach ($data['permissions'] as $permission) { foreach ($data['permissions'] as $permission) {
$options[$permission] = str($permission)->headline(); $options[$permission] = str($permission)->headline();
$descriptions[$permission] = trans('server/users.permissions.' . $data['name'] . '_' . str($permission)->replace('-', '_')); $descriptions[$permission] = trans('server/user.permissions.' . $data['name'] . '_' . str($permission)->replace('-', '_'));
$permissionsArray[$data['name']][] = $permission; $permissionsArray[$data['name']][] = $permission;
} }
$tabs[] = Tab::make(str($data['name'])->headline()) $tabs[] = Tab::make(str($data['name'])->headline())
->schema([ ->schema([
Section::make() Section::make()
->description(trans('server/users.permissions.' . $data['name'] . '_desc')) ->description(trans('server/user.permissions.' . $data['name'] . '_desc'))
->icon($data['icon']) ->icon($data['icon'])
->schema([ ->schema([
CheckboxList::make($data['name']) CheckboxList::make($data['name'])
@ -121,30 +121,33 @@ class UserResource extends Resource
->alignCenter()->circular() ->alignCenter()->circular()
->defaultImageUrl(fn (User $user) => Filament::getUserAvatarUrl($user)), ->defaultImageUrl(fn (User $user) => Filament::getUserAvatarUrl($user)),
TextColumn::make('username') TextColumn::make('username')
->label(trans('server/user.username'))
->searchable(), ->searchable(),
TextColumn::make('email') TextColumn::make('email')
->label(trans('server/user.email'))
->searchable(), ->searchable(),
TextColumn::make('permissions') TextColumn::make('permissions')
->label(trans('server/user.permissions.title'))
->state(fn (User $user) => count($server->subusers->where('user_id', $user->id)->first()->permissions)), ->state(fn (User $user) => count($server->subusers->where('user_id', $user->id)->first()->permissions)),
]) ])
->actions([ ->actions([
DeleteAction::make() DeleteAction::make()
->label('Remove User') ->label(trans('server/user.delete'))
->hidden(fn (User $user) => auth()->user()->id === $user->id) ->hidden(fn (User $user) => auth()->user()->id === $user->id)
->action(function (User $user, SubuserDeletionService $subuserDeletionService) use ($server) { ->action(function (User $user, SubuserDeletionService $subuserDeletionService) use ($server) {
$subuser = $server->subusers->where('user_id', $user->id)->first(); $subuser = $server->subusers->where('user_id', $user->id)->first();
$subuserDeletionService->handle($subuser, $server); $subuserDeletionService->handle($subuser, $server);
Notification::make() Notification::make()
->title('User Deleted!') ->title(trans('server/user.notification_delete'))
->success() ->success()
->send(); ->send();
}), }),
EditAction::make() EditAction::make()
->label('Edit User') ->label(trans('server/user.edit'))
->hidden(fn (User $user) => auth()->user()->id === $user->id) ->hidden(fn (User $user) => auth()->user()->id === $user->id)
->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_UPDATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_UPDATE, $server))
->modalHeading(fn (User $user) => 'Editing ' . $user->email) ->modalHeading(fn (User $user) => trans('server/user.editing', ['user' => $user->email]))
->action(function (array $data, SubuserUpdateService $subuserUpdateService, User $user) use ($server) { ->action(function (array $data, SubuserUpdateService $subuserUpdateService, User $user) use ($server) {
$subuser = $server->subusers->where('user_id', $user->id)->first(); $subuser = $server->subusers->where('user_id', $user->id)->first();
@ -158,7 +161,7 @@ class UserResource extends Resource
$subuserUpdateService->handle($subuser, $server, $permissions); $subuserUpdateService->handle($subuser, $server, $permissions);
Notification::make() Notification::make()
->title('User Updated!') ->title(trans('server/user.notification_edit'))
->success() ->success()
->send(); ->send();
@ -185,7 +188,7 @@ class UserResource extends Resource
]), ]),
Actions::make([ Actions::make([
Action::make('assignAll') Action::make('assignAll')
->label('Assign All') ->label(trans('server/user.assign_all'))
->action(function (Set $set) use ($permissionsArray) { ->action(function (Set $set) use ($permissionsArray) {
$permissions = $permissionsArray; $permissions = $permissionsArray;
foreach ($permissions as $key => $value) { foreach ($permissions as $key => $value) {
@ -231,4 +234,9 @@ class UserResource extends Resource
'index' => Pages\ListUsers::route('/'), 'index' => Pages\ListUsers::route('/'),
]; ];
} }
public static function getNavigationLabel(): string
{
return trans('server/user.title');
}
} }

View File

@ -25,6 +25,7 @@ use Filament\Forms\Set;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
use Filament\Support\Enums\IconSize; use Filament\Support\Enums\IconSize;
use Illuminate\Contracts\Support\Htmlable;
class ListUsers extends ListRecords class ListUsers extends ListRecords
{ {
@ -48,14 +49,14 @@ class ListUsers extends ListRecords
foreach ($data['permissions'] as $permission) { foreach ($data['permissions'] as $permission) {
$options[$permission] = str($permission)->headline(); $options[$permission] = str($permission)->headline();
$descriptions[$permission] = trans('server/users.permissions.' . $data['name'] . '_' . str($permission)->replace('-', '_')); $descriptions[$permission] = trans('server/user.permissions.' . $data['name'] . '_' . str($permission)->replace('-', '_'));
$permissionsArray[$data['name']][] = $permission; $permissionsArray[$data['name']][] = $permission;
} }
$tabs[] = Tab::make(str($data['name'])->headline()) $tabs[] = Tab::make(str($data['name'])->headline())
->schema([ ->schema([
Section::make() Section::make()
->description(trans('server/users.permissions.' . $data['name'] . '_desc')) ->description(trans('server/user.permissions.' . $data['name'] . '_desc'))
->icon($data['icon']) ->icon($data['icon'])
->schema([ ->schema([
CheckboxList::make($data['name']) CheckboxList::make($data['name'])
@ -72,7 +73,7 @@ class ListUsers extends ListRecords
Actions\CreateAction::make('invite') Actions\CreateAction::make('invite')
->hiddenLabel()->iconButton()->iconSize(IconSize::Large) ->hiddenLabel()->iconButton()->iconSize(IconSize::Large)
->icon('tabler-user-plus') ->icon('tabler-user-plus')
->tooltip('Invite User') ->tooltip(trans('server/user.invite_user'))
->createAnother(false) ->createAnother(false)
->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_CREATE, $server)) ->authorize(fn () => auth()->user()->can(Permission::ACTION_USER_CREATE, $server))
->form([ ->form([
@ -86,6 +87,7 @@ class ListUsers extends ListRecords
]) ])
->schema([ ->schema([
TextInput::make('email') TextInput::make('email')
->label(trans('server/user.email'))
->email() ->email()
->inlineLabel() ->inlineLabel()
->columnSpan([ ->columnSpan([
@ -97,7 +99,7 @@ class ListUsers extends ListRecords
->required(), ->required(),
assignAll::make([ assignAll::make([
Action::make('assignAll') Action::make('assignAll')
->label('Assign All') ->label(trans('server/user.assign_all'))
->action(function (Set $set, Get $get) use ($permissionsArray) { ->action(function (Set $set, Get $get) use ($permissionsArray) {
$permissions = $permissionsArray; $permissions = $permissionsArray;
foreach ($permissions as $key => $value) { foreach ($permissions as $key => $value) {
@ -117,8 +119,8 @@ class ListUsers extends ListRecords
->schema($tabs), ->schema($tabs),
]), ]),
]) ])
->modalHeading('Invite User') ->modalHeading(trans('server/user.invite_user'))
->modalSubmitActionLabel('Invite') ->modalSubmitActionLabel(trans('server/user.action'))
->action(function (array $data, SubuserCreationService $service) use ($server) { ->action(function (array $data, SubuserCreationService $service) use ($server) {
$email = strtolower($data['email']); $email = strtolower($data['email']);
@ -140,12 +142,12 @@ class ListUsers extends ListRecords
]); ]);
Notification::make() Notification::make()
->title('User Invited!') ->title(trans('server/user.notification_add'))
->success() ->success()
->send(); ->send();
} catch (Exception $exception) { } catch (Exception $exception) {
Notification::make() Notification::make()
->title('Failed') ->title(trans('server/user.notification_failed'))
->body($exception->getMessage()) ->body($exception->getMessage())
->danger() ->danger()
->send(); ->send();
@ -160,4 +162,9 @@ class ListUsers extends ListRecords
{ {
return []; return [];
} }
public function getTitle(): string|Htmlable
{
return trans('server/user.title');
}
} }

View File

@ -80,6 +80,6 @@ class ServerCpuChart extends ChartWidget
public function getHeading(): string public function getHeading(): string
{ {
return 'CPU'; return trans('server/console.labels.cpu');
} }
} }

View File

@ -80,6 +80,6 @@ class ServerMemoryChart extends ChartWidget
public function getHeading(): string public function getHeading(): string
{ {
return 'Memory'; return trans('server/console.labels.memory');
} }
} }

View File

@ -112,6 +112,6 @@ class ServerNetworkChart extends ChartWidget
{ {
$lastData = collect(cache()->get("servers.{$this->server->id}.network"))->last(); $lastData = collect(cache()->get("servers.{$this->server->id}.network"))->last();
return 'Network - ↓' . convert_bytes_to_readable($lastData->rx_bytes ?? 0) . ' - ↑' . convert_bytes_to_readable($lastData->tx_bytes ?? 0); return trans('server/console.labels.network') . ' - ↓' . convert_bytes_to_readable($lastData->rx_bytes ?? 0) . ' - ↑' . convert_bytes_to_readable($lastData->tx_bytes ?? 0);
} }
} }

View File

@ -20,14 +20,14 @@ class ServerOverview extends StatsOverviewWidget
protected function getStats(): array protected function getStats(): array
{ {
return [ return [
SmallStatBlock::make('Name', $this->server->name) SmallStatBlock::make(trans('server/console.labels.name'), $this->server->name)
->copyOnClick(fn () => request()->isSecure()), ->copyOnClick(fn () => request()->isSecure()),
SmallStatBlock::make('Status', $this->status()), SmallStatBlock::make(trans('server/console.labels.status'), $this->status()),
SmallStatBlock::make('Address', $this->server?->allocation->address ?? 'None') SmallStatBlock::make(trans('server/console.labels.address'), $this->server?->allocation->address ?? 'None')
->copyOnClick(fn () => request()->isSecure()), ->copyOnClick(fn () => request()->isSecure()),
SmallStatBlock::make('CPU', $this->cpuUsage()), SmallStatBlock::make(trans('server/console.labels.cpu'), $this->cpuUsage()),
SmallStatBlock::make('Memory', $this->memoryUsage()), SmallStatBlock::make(trans('server/console.labels.memory'), $this->memoryUsage()),
SmallStatBlock::make('Disk', $this->diskUsage()), SmallStatBlock::make(trans('server/console.labels.disk'), $this->diskUsage()),
]; ];
} }

View File

@ -30,34 +30,34 @@ class ServerEntry extends Component
<h2 class="text-xl font-bold"> <h2 class="text-xl font-bold">
{{ $server->name }} {{ $server->name }}
<span class="dark:text-gray-400"> <span class="dark:text-gray-400">
(Loading) ({{ trans('server/dashboard.loading') }})
</span> </span>
</h2> </h2>
</div> </div>
<div class="flex justify-between text-center items-center gap-4"> <div class="flex justify-between text-center items-center gap-4">
<div> <div>
<p class="text-sm dark:text-gray-400">CPU</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.cpu') }}</p>
<p class="text-md font-semibold">{{ Number::format(0, precision: 2, locale: auth()->user()->language ?? 'en') . '%' }}</p> <p class="text-md font-semibold">{{ Number::format(0, precision: 2, locale: auth()->user()->language ?? 'en') . '%' }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::CPULimit) }}</p> <p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::CPULimit) }}</p>
</div> </div>
<div> <div>
<p class="text-sm dark:text-gray-400">Memory</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.memory') }}</p>
<p class="text-md font-semibold">{{ convert_bytes_to_readable(0, decimals: 2) }}</p> <p class="text-md font-semibold">{{ convert_bytes_to_readable(0, decimals: 2) }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::MemoryLimit) }}</p> <p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::MemoryLimit) }}</p>
</div> </div>
<div> <div>
<p class="text-sm dark:text-gray-400">Disk</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.disk') }}</p>
<p class="text-md font-semibold">{{ convert_bytes_to_readable(0, decimals: 2) }}</p> <p class="text-md font-semibold">{{ convert_bytes_to_readable(0, decimals: 2) }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::DiskLimit) }}</p> <p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::DiskLimit) }}</p>
</div> </div>
<div class="hidden sm:block"> <div class="hidden sm:block">
<p class="text-sm dark:text-gray-400">Network</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.network') }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-md font-semibold">{{ $server->allocation?->address ?? 'None' }} </p> <p class="text-md font-semibold">{{ $server->allocation?->address ?? trans('server/dashboard.none') }} </p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -42,7 +42,7 @@ class AppPanelProvider extends PanelProvider
->passwordReset() ->passwordReset()
->userMenuItems([ ->userMenuItems([
MenuItem::make() MenuItem::make()
->label('Admin') ->label(trans('profile.admin'))
->url('/admin') ->url('/admin')
->icon('tabler-arrow-forward') ->icon('tabler-arrow-forward')
->sort(5) ->sort(5)

View File

@ -55,14 +55,14 @@ class ServerPanelProvider extends PanelProvider
->url(fn () => ListServers::getUrl(panel: 'app')) ->url(fn () => ListServers::getUrl(panel: 'app'))
->sort(6), ->sort(6),
MenuItem::make() MenuItem::make()
->label('Admin') ->label(trans('profile.admin'))
->icon('tabler-arrow-forward') ->icon('tabler-arrow-forward')
->url(fn () => Filament::getPanel('admin')->getUrl()) ->url(fn () => Filament::getPanel('admin')->getUrl())
->sort(5) ->sort(5)
->visible(fn (): bool => auth()->user()->canAccessPanel(Filament::getPanel('admin'))), ->visible(fn (): bool => auth()->user()->canAccessPanel(Filament::getPanel('admin'))),
]) ])
->navigationItems([ ->navigationItems([
NavigationItem::make('Open in Admin') NavigationItem::make(trans('server/console.open_in_admin'))
->url(fn () => EditServer::getUrl(['record' => Filament::getTenant()], panel: 'admin')) ->url(fn () => EditServer::getUrl(['record' => Filament::getTenant()], panel: 'admin'))
->visible(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin')) && auth()->user()->can('view server', Filament::getTenant())) ->visible(fn () => auth()->user()->canAccessPanel(Filament::getPanel('admin')) && auth()->user()->can('view server', Filament::getTenant()))
->icon('tabler-arrow-back') ->icon('tabler-arrow-back')

View File

@ -12,6 +12,7 @@ return [
'customization' => 'Customization', 'customization' => 'Customization',
], ],
'username' => 'Username', 'username' => 'Username',
'admin' => 'Admin',
'exit_admin' => 'Exit Admin', 'exit_admin' => 'Exit Admin',
'email' => 'Email', 'email' => 'Email',
'password' => 'Password', 'password' => 'Password',

View File

@ -0,0 +1,11 @@
<?php
return [
'title' => 'Activity',
'event' => 'Event',
'user' => 'User',
'deleted_user' => 'Deleted User',
'system' => 'System',
'timestamp' => 'Timestamp',
'metadata' => 'Metadata',
];

50
lang/en/server/backup.php Normal file
View File

@ -0,0 +1,50 @@
<?php
return [
'title' => 'Backups',
'empty' => 'No Backups',
'size' => 'Size',
'created_at' => 'Created at',
'status' => 'Status',
'is_locked' => 'Lock Status',
'backup_status' => [
'in_progress' => 'In Progress',
'successful' => 'Successful',
'failed' => 'Failed',
],
'actions' => [
'create' => [
'title' => 'Create Backup',
'limit' => 'Backup Limit Reached',
'created' => ':name created',
'notification_success' => 'Backup Created Successfully',
'notification_fail' => 'Backup Creation Failed',
'name' => 'Name',
'ignored' => 'Ignored Files & Directories',
'locked' => 'Locked?',
'lock_helper' => 'Prevents this backup from being deleted until explicitly unlocked.',
],
'lock' => [
'lock' => 'Lock',
'unlock' => 'Unlock',
],
'download' => 'Download',
'restore' => [
'title' => 'Restore',
'helper' => 'Your server will be stopped. You will not be able to control the power state, access the file manager, or create additional backups until this process is completed.',
'delete_all' => 'Delete all files before restoring backup?',
'notification_started' => 'Restoring Backup',
'notification_success' => 'Backup Restored Successfully',
'notification_fail' => 'Backup Restore Failed',
'notification_fail_body_1' => 'This server is not currently in a state that allows for a backup to be restored.',
'notification_fail_body_2' => 'This backup cannot be restored at this time: not completed or failed.',
],
'delete' => [
'title' => 'Delete Backup',
'description' => 'Do you wish to delete :backup?',
'notification_success' => 'Backup Deleted',
'notification_fail' => 'Could not delete backup',
'notification_fail_body' => 'Connection to node failed. Please try again.',
],
],
];

View File

@ -0,0 +1,39 @@
<?php
return [
'title' => 'Console',
'command' => 'Type a command...',
'command_blocked' => 'Server Offline...',
'command_blocked_title' => 'Can\'t send command when the server is Offline',
'open_in_admin' => 'Open in Admin',
'power_actions' => [
'start' => 'Start',
'stop' => 'Stop',
'restart' => 'Restart',
'kill' => 'Kill',
'kill_tooltip' => 'This can result in data corruption and/or data loss!',
],
'labels' => [
'cpu' => 'CPU',
'memory' => 'Memory',
'network' => 'Network',
'disk' => 'Disk',
'name' => 'Name',
'status' => 'Status',
'address' => 'Address',
'unavailable' => 'Unavailable',
],
'status' => [
'created' => 'Created',
'starting' => 'Starting',
'running' => 'Running',
'restarting' => 'Restarting',
'exited' => 'Exited',
'paused' => 'Paused',
'dead' => 'Dead',
'removing' => 'Removing',
'stopping' => 'Stopping',
'offline' => 'Offline',
'missing' => 'Missing',
],
];

View File

@ -0,0 +1,25 @@
<?php
return [
'title' => 'Servers',
'list' => 'Server List',
'my_servers' => 'My Servers',
'other_servers' => 'Others\' Servers',
'all_servers' => 'All Servers',
'empty_own' => 'You don\'t own any servers!',
'empty_other' => 'You don\'t have access to any servers!',
'status' => 'Status',
'server' => 'Server',
'resource' => 'Resource',
'usage_limit' => 'Usage Limit: :resource',
'cpu' => 'CPU',
'memory' => 'Memory',
'disk' => 'Disk',
'network' => 'Network',
'none' => 'None',
'loading' => 'Loading...',
'power_actions' => 'Power Actions',
];

View File

@ -0,0 +1,21 @@
<?php
return [
'title' => 'Databases',
'create_database' => 'Create Database',
'limit' => 'Database limit reached',
'viewing' => 'Viewing: :database',
'host' => 'Host',
'database' => 'Database',
'username' => 'Username',
'password' => 'Password',
'remote' => 'Remote',
'created_at' => 'Created at',
'name' => 'Database Name',
'name_hint' => 'Leaving this blank will auto generate a random name',
'connections_from' => 'Connections From',
'max_connections' => 'Max Connections',
'database_host' => 'Database Host',
'database_host_select' => 'Select Database Host',
'jdbc' => 'JDBC Connection String',
];

82
lang/en/server/file.php Normal file
View File

@ -0,0 +1,82 @@
<?php
return [
'title' => 'Files',
'name' => 'Name',
'size' => 'Size',
'modified_at' => 'Modified at',
'actions' => [
'open' => 'Open',
'download' => 'Download',
'copy' => [
'title' => 'Copy',
'notification' => 'File Copied',
],
'upload' => [
'title' => 'Upload',
'from_files' => 'Upload Files',
'from_url' => 'Upload from URL',
'url' => 'URL',
],
'rename' => [
'title' => 'Rename',
'file_name' => 'File Name',
'notification' => 'File Renamed',
],
'move' => [
'title' => 'Move',
'directory' => 'Directory',
'directory_hint' => 'Enter the new directory, relative to the current directory.',
'new_location' => 'New Location',
'new_location_hint' => 'Enter the location of this file or folder, relative to the current directory.',
'notification' => 'File Moved',
'bulk_notification' => ':count Files were moved to :directory',
],
'permissions' => [
'title' => 'Permissions',
'read' => 'Read',
'write' => 'Write',
'execute' => 'Execute',
'owner' => 'Owner',
'group' => 'Group',
'public' => 'Public',
'notification' => 'Permissions changed to :mode',
],
'archive' => [
'title' => 'Archive',
'archive_name' => 'Archive Name',
'notification' => 'Archive Created',
],
'unarchive' => [
'title' => 'Unarchive',
'notification' => 'Unarchive Completed',
],
'new_file' => [
'title' => 'New file',
'file_name' => 'New file name',
'syntax' => 'Syntax Highlighting',
'create' => 'Create',
],
'new_folder' => [
'title' => 'New folder',
'folder_name' => 'New folder name',
],
'global_search' => [
'title' => 'Global Search',
'search_term' => 'Search term',
'search_term_placeholder' => 'Enter a search term, ex. *.txt',
'search' => 'Search',
],
'delete' => [
'notification' => 'File Deleted',
'bulk_notification' => ':count files were deleted',
],
'edit' => [
'title' => 'Editing: :file',
'save_close' => 'Save & Close',
'save' => 'Save',
'cancel' => 'Cancel',
'notification' => 'File Saved',
],
],
];

View File

@ -0,0 +1,15 @@
<?php
return [
'title' => 'Network',
'add' => 'Add Allocation',
'limit' => 'Allocation limit reached',
'address' => 'Address',
'port' => 'Port',
'notes' => 'Notes',
'no_notes' => 'No Notes',
'make_primary' => 'Make Primary',
'primary' => 'Primary',
'make' => 'Make',
'delete' => 'Delete',
];

107
lang/en/server/schedule.php Normal file
View File

@ -0,0 +1,107 @@
<?php
return [
'title' => 'Schedules',
'new' => 'New Schedule',
'edit' => 'Edit Schedule',
'save' => 'Save Schedule',
'delete' => 'Delete Schedule',
'import' => 'Import Schedule',
'export' => 'Export Schedule',
'name' => 'Name',
'cron' => 'Cron',
'status' => 'Status',
'inactive' => 'Inactive',
'processing' => 'Processing',
'active' => 'Active',
'no_tasks' => 'No Tasks',
'run_now' => 'Run Now',
'online_only' => 'Only When Online',
'last_run' => 'Last Run',
'next_run' => 'Next Run',
'never' => 'Never',
'cancel' => 'Cancel',
'only_online' => 'Only when Server is Online?',
'only_online_hint' => 'Only execute this schedule when the server is in a running state.',
'enabled' => 'Enable Schedule?',
'enabled_hint' => 'This schedule will be executed automatically if enabled.',
'cron_body' => 'Please keep in mind that the cron inputs below always assume UTC.',
'cron_timezone' => 'Next run in your timezone (:timezone): <b> :next_run </b>',
'time' => [
'minute' => 'Minute',
'hour' => 'Hour',
'day' => 'Day',
'week' => 'Week',
'month' => 'Month',
'day_of_month' => 'Day of Month',
'day_of_week' => 'Day of Week',
'hourly' => 'Hourly',
'daily' => 'Daily',
'weekly_mon' => 'Weekly (Monday)',
'weekly_sun' => 'Weekly (Sunday)',
'monthly' => 'Monthly',
'every_min' => 'Every x minutes',
'every_hour' => 'Every x hours',
'every_day' => 'Every x days',
'every_week' => 'Every x weeks',
'every_month' => 'Every x months',
'every_day_of_week' => 'Every x day of week',
'every' => 'Every',
'minutes' => 'Minutes',
'hours' => 'Hours',
'days' => 'Days',
'months' => 'Months',
'monday' => 'Monday',
'tuesday' => 'Tuesday',
'wednesday' => 'Wednesday',
'thursday' => 'Thursday',
'friday' => 'Friday',
'saturday' => 'Saturday',
'sunday' => 'Sunday',
],
'tasks' => [
'title' => 'Tasks',
'create' => 'Create Task',
'limit' => 'Task Limit Reached',
'action' => 'Action',
'payload' => 'Payload',
'time_offset' => 'Time Offset',
'seconds' => 'Seconds',
'continue_on_failure' => 'Continue On Failure',
'actions' => [
'title' => 'Action',
'power' => [
'title' => 'Send Power Action',
'action' => 'Power action',
'start' => 'Start',
'stop' => 'Stop',
'restart' => 'Restart',
'kill' => 'Kill',
],
'command' => [
'title' => 'Send Command',
'command' => 'Command',
],
'backup' => [
'title' => 'Create Backup',
'files_to_ignore' => 'Files to Ignore',
],
'delete' => [
'title' => 'Delete Files',
'files_to_delete' => 'Files to Delete',
],
],
],
'notification_invalid_cron' => 'The cron data provided does not evaluate to a valid expression',
];

View File

@ -0,0 +1,51 @@
<?php
return [
'title' => 'Settings',
'server_info' => [
'title' => 'Server Information',
'information' => 'Information',
'name' => 'Server Name',
'notification_name' => 'Updated Server Name',
'description' => 'Server Description',
'notification_description' => 'Updated Server Description',
'failed' => 'Failed',
'uuid' => 'Server UUID',
'id' => 'Server ID',
'limits' => [
'title' => 'Limits',
'unlimited' => 'Unlimited',
'of' => 'of',
'cpu' => 'CPU',
'memory' => 'Memory',
'disk' => 'Disk Space',
'backups' => 'Backups',
'databases' => 'Databases',
'allocations' => 'Allocations',
'no_allocations' => 'No Additional Allocations',
],
],
'node_info' => [
'title' => 'Node Information',
'name' => 'Node Name',
'sftp' => [
'title' => 'SFTP Information',
'connection' => 'Connection',
'action' => 'Connect to SFTP',
'username' => 'Username',
'password' => 'Password',
'password_body' => 'Your SFTP password is the same as the password you use to access this panel.',
],
],
'reinstall' => [
'title' => 'Reinstall Server',
'body' => 'Reinstalling your server will stop it, and then re-run the installation script that initially set it up.',
'body2' => 'Some files may be deleted or modified during this process, please back up your data before continuing.',
'action' => 'Reinstall',
'modal' => 'Are you sure you want to reinstall the server?',
'modal_description' => 'Some files may be deleted or modified during this process, please back up your data before continuing.',
'yes' => 'Yes, Reinstall',
'notification_start' => 'Reinstall Started',
'notification_fail' => 'Reinstall Failed',
],
];

View File

@ -0,0 +1,14 @@
<?php
return [
'title' => 'Startup',
'command' => 'Startup Command',
'preview' => 'Preview',
'docker_image' => 'Docker Image',
'notification_docker' => 'Docker Image Updated',
'notification_docker_body' => 'Restart the server to use the new image.',
'variables' => 'Server Variables',
'update' => 'Updated: :variable',
'fail' => 'Failed: :variable',
'validation_fail' => 'Validation Failed: :variable',
];

View File

@ -1,7 +1,22 @@
<?php <?php
return [ return [
'title' => 'Users',
'username' => 'Username',
'email' => 'Email',
'assign_all' => 'Assign all',
'invite_user' => 'Invite User',
'action' => 'Invite',
'remove' => 'Remove User',
'edit' => 'Edit User',
'editing' => 'Editing :user',
'delete' => 'Delete User',
'notification_add' => 'User Invited!',
'notification_edit' => 'User Updated!',
'notification_delete' => 'User Deleted!',
'notification_failed' => 'Failed to invite user!',
'permissions' => [ 'permissions' => [
'title' => 'Permissions',
'activity_desc' => 'Permissions that control a user\'s access to the server activity logs.', 'activity_desc' => 'Permissions that control a user\'s access to the server activity logs.',
'startup_desc' => 'Permissions that control a user\'s ability to view this server\'s startup parameters.', 'startup_desc' => 'Permissions that control a user\'s ability to view this server\'s startup parameters.',
'settings_desc' => 'Permissions that control a user\'s ability to modify this server\'s settings.', 'settings_desc' => 'Permissions that control a user\'s ability to modify this server\'s settings.',

View File

@ -30,8 +30,8 @@
class="w-full focus:outline-none focus:ring-0 border-none dark:bg-gray-900" class="w-full focus:outline-none focus:ring-0 border-none dark:bg-gray-900"
type="text" type="text"
:readonly="{{ $this->canSendCommand() ? 'false' : 'true' }}" :readonly="{{ $this->canSendCommand() ? 'false' : 'true' }}"
title="{{ $this->canSendCommand() ? '' : 'Can\'t send command when the server is Offline' }}" title="{{ $this->canSendCommand() ? '' : trans('server/console.command_blocked_title') }}"
placeholder="{{ $this->canSendCommand() ? 'Type a command...' : 'Server Offline...' }}" placeholder="{{ $this->canSendCommand() ? trans('server/console.command') : trans('server/console.command_blocked') }}"
wire:model="input" wire:model="input"
wire:keydown.enter="enter" wire:keydown.enter="enter"
wire:keydown.up.prevent="up" wire:keydown.up.prevent="up"

View File

@ -33,27 +33,27 @@
<div class="flex justify-between text-center items-center gap-4"> <div class="flex justify-between text-center items-center gap-4">
<div> <div>
<p class="text-sm dark:text-gray-400">CPU</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.cpu') }}</p>
<p class="text-md font-semibold">{{ $server->formatResource(\App\Enums\ServerResourceType::CPU) }}</p> <p class="text-md font-semibold">{{ $server->formatResource(\App\Enums\ServerResourceType::CPU) }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::CPULimit) }}</p> <p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::CPULimit) }}</p>
</div> </div>
<div> <div>
<p class="text-sm dark:text-gray-400">Memory</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.memory') }}</p>
<p class="text-md font-semibold">{{ $server->formatResource(\App\Enums\ServerResourceType::Memory) }}</p> <p class="text-md font-semibold">{{ $server->formatResource(\App\Enums\ServerResourceType::Memory) }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::MemoryLimit) }}</p> <p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::MemoryLimit) }}</p>
</div> </div>
<div> <div>
<p class="text-sm dark:text-gray-400">Disk</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.disk') }}</p>
<p class="text-md font-semibold">{{ $server->formatResource(\App\Enums\ServerResourceType::Disk) }}</p> <p class="text-md font-semibold">{{ $server->formatResource(\App\Enums\ServerResourceType::Disk) }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::DiskLimit) }}</p> <p class="text-xs dark:text-gray-400">{{ $server->formatResource(\App\Enums\ServerResourceType::DiskLimit) }}</p>
</div> </div>
<div class="hidden sm:block"> <div class="hidden sm:block">
<p class="text-sm dark:text-gray-400">Network</p> <p class="text-sm dark:text-gray-400">{{ trans('server/dashboard.network') }}</p>
<hr class="p-0.5"> <hr class="p-0.5">
<p class="text-md font-semibold">{{ $server->allocation?->address ?? 'None' }}</p> <p class="text-md font-semibold">{{ $server->allocation?->address ?? trans('server/dashboard.none') }}</p>
</div> </div>
</div> </div>
</div> </div>