🆙 Final fix delete storage link to fix news_images and logs 🆙

This commit is contained in:
Remco
2026-01-07 20:29:24 +01:00
parent 65ea6c167f
commit acf2d7e661
447 changed files with 208 additions and 66965 deletions
@@ -1,58 +0,0 @@
<?php
namespace App\Actions\Fortify\Controllers;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Routing\Controller;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Events\RecoveryCodeReplaced;
use Laravel\Fortify\Http\Requests\TwoFactorLoginRequest;
use Laravel\Fortify\Http\Responses\TwoFactorLoginResponse;
class TwoFactorAuthenticatedSessionController extends Controller
{
/**
* The guard implementation.
*
* @var StatefulGuard
*/
protected $guard;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(StatefulGuard $guard)
{
$this->guard = $guard;
}
/**
* Attempt to authenticate a new session using the two factor authentication code.
*/
public function store(TwoFactorLoginRequest $request): TwoFactorLoginResponse
{
$user = $request->challengedUser();
if ($code = $request->validRecoveryCode()) {
$user->replaceRecoveryCode($code);
event(new RecoveryCodeReplaced($user, $code));
} elseif (! $request->hasValidCode()) {
throw ValidationException::withMessages([
'code' => __('Invalid Two Factor Authentication code'),
]);
}
$this->guard->login($user, $request->remember());
$request->session()->regenerate();
$user->update([
'ip_current' => $request->ip(),
]);
return app(TwoFactorLoginResponse::class);
}
}
@@ -1,158 +0,0 @@
<?php
namespace App\Actions\Fortify;
use App\Actions\Fortify\Rules\PasswordValidationRules;
use App\Models\Miscellaneous\WebsiteBetaCode;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use App\Rules\BetaCodeRule;
use App\Rules\GoogleRecaptchaRule;
use App\Rules\WebsiteWordfilterRule;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use RyanChandler\LaravelCloudflareTurnstile\Rules\Turnstile;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*/
public function create(array $input)
{
if (setting('disable_registration') ?: '0' === '1') {
throw ValidationException::withMessages([
'registration' => __('Registration is disabled.'),
]);
}
$ip = request()?->ip();
if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
throw ValidationException::withMessages([
'registration' => __('Your IP address seems to be invalid'),
]);
}
$matchingIpCount = User::query()
->where('ip_current', '=', $ip)
->orWhere('ip_register', '=', $ip)
->count();
if ($matchingIpCount >= (int) (setting('max_accounts_per_ip') ?: 99)) {
throw ValidationException::withMessages([
'registration' => __('You have reached the max amount of allowed account'),
]);
}
$this->validate($input);
$user = User::create([
'username' => $input['username'],
'mail' => $input['mail'],
'password' => Hash::make($input['password']),
'account_created' => time(),
'last_login' => time(),
'motto' => setting('start_motto') ?: 'Welcome to the hotel!',
'look' => setting('start_look') ?: 'hr-100-61.hd-180-1.ch-210-66.lg-270-110.sh-305-62',
'credits' => setting('start_credits') ?: 1000,
'ip_register' => $ip,
'ip_current' => $ip,
'auth_ticket' => '',
'home_room' => (int) (setting('hotel_home_room') ?: 0),
]);
$user->update([
'referral_code' => sprintf('%s%s', $user->id, Str::random(8)),
]);
if (setting('requires_beta_code') !== '' && setting('requires_beta_code') !== '0') {
WebsiteBetaCode::where('code', '=', $input['beta_code'])->update([
'user_id' => $user->id,
]);
}
// Referral
if (isset($input['referral_code'])) {
$referralUser = User::query()
->where('referral_code', '=', $input['referral_code'])
->first();
if (is_null($referralUser)) {
return redirect(RouteServiceProvider::HOME);
}
// If same IP skip referral incrementation
if ($referralUser->ip_current == $user->ip_current || $referralUser->ip_register == $user->ip_register) {
return redirect(RouteServiceProvider::HOME);
}
$referralUser->referrals()->updateOrCreate(['user_id' => $referralUser->id], [
'referrals_total' => $referralUser->referrals != null ? $referralUser->referrals->referrals_total += 1 : 1,
]);
$referralUser->userReferrals()->create([
'referred_user_id' => $user->id,
'referred_user_ip' => $ip,
]);
}
if (setting('enable_discord_webhook') === '1') {
$this->sendDiscordWebhook($user->username, $user->ip_register, $user->mail);
}
return $user;
}
private function validate(array $inputs): array
{
$rules = [
'username' => ['required', 'string', sprintf('regex:%s', setting('username_regex') ?: '/^[a-zA-Z0-9_.-]+$/'), 'max:25', Rule::unique('users'), new WebsiteWordfilterRule],
'mail' => ['required', 'string', 'email', 'max:255', Rule::unique('users')],
'password' => $this->passwordRules(),
'beta_code' => ['sometimes', 'string', new BetaCodeRule],
'terms' => ['required', 'accepted'],
'g-recaptcha-response' => ['sometimes', 'string', new GoogleRecaptchaRule],
'cf-turnstile-response' => [app(Turnstile::class)],
];
$messages = [
'g-recaptcha-response.required' => __('The Google recaptcha must be completed'),
'g-recaptcha-response.string' => __('The google recaptcha was submitted with an invalid type'),
];
return Validator::make($inputs, $rules, $messages)->validate();
}
private function sendDiscordWebhook(string $username, string $ip, string $email): void
{
if (setting('discord_webhook_url') === '') {
Log::error('Discord webhook url not provided', ['Please provide a discord webhook url before being able to send any webhook requests.']);
return;
}
$request = Http::asJson()->post(setting('discord_webhook_url'), [
'username' => sprintf('%s Bot', setting('hotel_name')),
'content' => "User: {$username} has just registered, with the IP: {$ip} and E-mail: {$email}",
]);
// Log the error in-case webhook wasn't sent
if (! $request->successful()) {
Log::error('Failed to send Discord webhook notification', [
'username' => $username,
'ip' => $ip,
'email' => $email,
'response_status' => $request->status(),
'response_body' => $request->body(),
]);
}
}
}
@@ -1,16 +0,0 @@
<?php
namespace App\Actions\Fortify;
class DisableTwoFactorAuthentication extends \Laravel\Fortify\Actions\DisableTwoFactorAuthentication
{
#[\Override]
public function __invoke($user)
{
$user->forceFill([
'two_factor_secret' => null,
'two_factor_recovery_codes' => null,
'two_factor_confirmed' => false,
])->save();
}
}
@@ -1,194 +0,0 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use App\Rules\GoogleRecaptchaRule;
use Illuminate\Auth\Events\Failed;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Events\TwoFactorAuthenticationChallenged;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\LoginRateLimiter;
use Laravel\Fortify\TwoFactorAuthenticatable;
use RyanChandler\LaravelCloudflareTurnstile\Rules\Turnstile;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfTwoFactorAuthenticatable
{
/**
* The guard implementation.
*
* @var StatefulGuard
*/
protected $guard;
/**
* The login rate limiter instance.
*
* @var LoginRateLimiter
*/
protected $limiter;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct(StatefulGuard $guard, LoginRateLimiter $limiter)
{
$this->guard = $guard;
$this->limiter = $limiter;
}
/**
* Handle the incoming request.
*
* @return mixed
*/
public function handle(Request $request, callable $next)
{
$user = $this->validateCredentials($request);
if (Fortify::confirmsTwoFactorAuthentication()) {
if ($user?->two_factor_secret &&
! is_null($user?->two_factor_confirmed_at) &&
in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) {
return $this->twoFactorChallengeResponse($request, $user);
} else {
return $next($request);
}
}
if ($user?->two_factor_secret &&
in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) {
return $this->twoFactorChallengeResponse($request, $user);
}
return $next($request);
}
/**
* Attempt to validate the incoming credentials.
*
* @return mixed
*/
protected function validateCredentials(Request $request)
{
if (Fortify::$authenticateUsingCallback) {
return tap(call_user_func(Fortify::$authenticateUsingCallback, $request), function ($user) use ($request): void {
if (! $user) {
$this->fireFailedEvent($request);
$this->throwFailedAuthenticationException($request);
}
});
}
$model = $this->guard->getProvider()->getModel();
return tap($model::where(Fortify::username(), $request->{Fortify::username()})->first(), function ($user) use ($request): void {
// Update the users password to bcrypt, if they previously used md5
if ($user && config('habbo.site.convert_passwords')) {
$this->convertUserPassword($user, $request->input('password'));
}
if (! $user || ! $this->guard->getProvider()->validateCredentials($user, ['password' => $request->password])) {
$this->fireFailedEvent($request, $user);
$this->throwFailedAuthenticationException($request);
}
$this->validate($request);
$user = User::select('id', 'password', 'rank')
->where('username', '=', $request->input('username'))
->first();
if (setting('maintenance_enabled') === '1' && setting('min_maintenance_login_rank') > $user->rank) {
throw ValidationException::withMessages([
'username' => __('Only staff can login during maintenance!'),
]);
}
});
}
/**
* Throw a failed authentication validation exception.
*
*
* @throws ValidationException
*/
protected function throwFailedAuthenticationException(Request $request): void
{
$this->limiter->increment($request);
throw ValidationException::withMessages([
Fortify::username() => [trans('auth.failed')],
])->errorBag('login');
}
/**
* Fire the failed authentication attempt event with the given arguments.
*/
protected function fireFailedEvent(Request $request, ?Authenticatable $user = null): void
{
event(new Failed(config('fortify.guard'), $user, [
Fortify::username() => $request->{Fortify::username()},
'password' => $request->password,
]));
}
/**
* Get the two factor authentication enabled response.
*
* @param mixed $user
*/
protected function twoFactorChallengeResponse(Request $request, $user): Response
{
$request->session()->put([
'login.id' => $user->getKey(),
'login.remember' => $request->filled('remember'),
]);
event(new \Laravel\Fortify\Events\TwoFactorAuthenticationChallenged($user));
return $request->wantsJson()
? response()->json(['two_factor' => true])
: to_route('two-factor.login');
}
private function convertUserPassword(User $user, string $password)
{
if ($user->password == md5($password)) {
$user->update([
'password' => Hash::make($password),
]);
}
}
private function validate(Request $request): array
{
$rules = [];
if (setting('google_recaptcha_enabled') !== '' && setting('google_recaptcha_enabled') !== '0') {
$rules['g-recaptcha-response'] = ['required', 'string', new GoogleRecaptchaRule];
}
if (setting('cloudflare_turnstile_enabled') !== '' && setting('cloudflare_turnstile_enabled') !== '0') {
$rules['cf-turnstile-response'] = ['required', app(Turnstile::class)];
}
$messages = [
'g-recaptcha-response.required' => __('The Google reCAPTCHA must be completed'),
'g-recaptcha-response.string' => __('The Google reCAPTCHA was submitted with an invalid type'),
'cf-turnstile-response.required' => __('The Cloudflare Turnstile response is required'),
];
return Validator::make($request->all(), $rules, $messages)->validate();
}
}
@@ -1,10 +0,0 @@
<?php
namespace App\Actions\Fortify;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
class RedirectIfTwoFactorConfirmed extends RedirectIfTwoFactorAuthenticatable
{
// This class can use the default behavior from the parent class
}
@@ -1,28 +0,0 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param mixed $user
*/
public function reset($user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}
@@ -1,16 +0,0 @@
<?php
namespace App\Actions\Fortify\Rules;
use App\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*/
protected function passwordRules(): array
{
return ['required', 'string', new Password, 'confirmed'];
}
}
@@ -1,31 +0,0 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param mixed $user
*/
public function update($user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}
@@ -1,57 +0,0 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param mixed $user
*/
public function update($user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email &&
$user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param mixed $user
*/
protected function updateVerifiedUser($user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}
-38
View File
@@ -1,38 +0,0 @@
<?php
namespace App\Actions;
use App\Enums\CurrencyTypes;
use App\Services\RconService;
class SendCurrency
{
public function __construct(protected RconService $rcon) {}
public function execute($user, string $type, ?int $amount)
{
if (! $amount && $amount <= 0) {
return false;
}
if ($this->rcon->isConnected) {
match ($type) {
'credits' => $this->rcon->giveCredits($user, $amount),
'duckets' => $this->rcon->giveDuckets($user, $amount),
'diamonds' => $this->rcon->giveDiamonds($user, $amount),
'points' => $this->rcon->giveGotw($user, $amount),
default => false,
};
} else {
match ($type) {
'credits' => $user->increment('credits', $amount),
'duckets' => $user->currencies()->where('type', CurrencyTypes::Duckets)->increment('amount', $amount),
'diamonds' => $user->currencies()->where('type', CurrencyTypes::Diamonds)->increment('amount', $amount),
'points' => $user->currencies()->where('type', CurrencyTypes::Points)->increment('amount', $amount),
default => false,
};
}
return null;
}
}
-28
View File
@@ -1,28 +0,0 @@
<?php
namespace App\Actions;
use App\Models\User;
use App\Services\RconService;
class SendFurniture
{
public function __construct(private readonly RconService $rcon) {}
public function execute(User $user, array $furniture): void
{
foreach ($furniture as $furni) {
if ($this->rcon->isConnected) {
for ($i = 0; $i < $furni['amount']; $i++) {
$this->rcon->sendGift($user, $furni['item_id'], 'Thank you for supporting ' . setting('hotel_name'));
}
} else {
for ($i = 0; $i < $furni['amount']; $i++) {
$user->items()->create([
'item_id' => $furni['item_id'],
]);
}
}
}
}
}
-34
View File
@@ -1,34 +0,0 @@
<?php
namespace App\Actions;
class UserActions
{
public function updateUsername($user, $username): void
{
$user->update([
'username' => $username,
]);
}
public function updateEmail($user, $email): void
{
$user->update([
'mail' => $email,
]);
}
public function updateMotto($user, $motto): void
{
$user->update([
'motto' => $motto,
]);
}
public function updateField($user, string $field, ?string $value): void
{
$user->update([
$field => $value,
]);
}
}
@@ -1,148 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Miscellaneous\WebsiteSetting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
class AtomSetupCommand extends Command
{
protected $signature = 'atom:setup {--auto=false}';
protected $description = 'Takes you through a basic setup, allowing you to define general settings';
private function progressInfo(int $step)
{
$this->info(sprintf('Step %s/13', $step));
$this->newLine();
}
public function handle(): void
{
Artisan::call('db:seed --class=WebsiteSettingsSeeder');
if ($this->option('auto') === 'false') {
$step = 1;
$this->progressInfo($step);
$step++;
$hotelName = $this->ask('Enter your hotel name');
WebsiteSetting::where('key', '=', 'hotel_name')->update([
'value' => empty($hotelName) ? 'Hotel' : $hotelName,
]);
$this->progressInfo($step);
$step++;
$colorMode = $this->choice('Enter your preferred CMS color mode', ['light', 'dark'], 0);
WebsiteSetting::where('key', '=', 'cms_color_mode')->update([
'value' => $colorMode,
]);
$this->progressInfo($step);
$step++;
$startCredits = $this->ask('Enter the amount of credits new users should start with: (default is 5000)');
WebsiteSetting::where('key', '=', 'start_credits')->update([
'value' => empty($startCredits) ? '5000' : $startCredits,
]);
$this->progressInfo($step);
$step++;
$startDuckets = $this->ask('Enter the amount of credits new users should start with: (default is 5000)');
WebsiteSetting::where('key', '=', 'start_duckets')->update([
'value' => empty($startDuckets) ? '5000' : $startDuckets,
]);
$this->progressInfo($step);
$step++;
$startDiamonds = $this->ask('Enter the amount of diamonds new users should start with: (default is 100)');
WebsiteSetting::where('key', '=', 'start_diamonds')->update([
'value' => empty($startDiamonds) ? '100' : $startDiamonds,
]);
$this->progressInfo($step);
$step++;
$startPoints = $this->ask('Enter the amount of points new users should start with (default is 0)');
WebsiteSetting::where('key', '=', 'start_points')->update([
'value' => empty($startPoints) ? '0' : $startPoints,
]);
$this->progressInfo($step);
$step++;
$maxAccountsPerIP = $this->ask('Enter the amount of accounts a user can register per IP address (default is 2)');
WebsiteSetting::where('key', '=', 'max_accounts_per_ip')->update([
'value' => empty($maxAccountsPerIP) ? '2' : $maxAccountsPerIP,
]);
$this->progressInfo($step);
$step++;
$recaptchaEnabled = $this->choice('Google ReCaptcha enabled: (Do not forget to add your keys to your .env file in-case you set this to 1)', ['0', '1'], 0);
WebsiteSetting::where('key', '=', 'google_recaptcha_enabled')->update([
'value' => $recaptchaEnabled,
]);
$this->progressInfo($step);
$step++;
$wordfilterEnabled = $this->choice('CMS wordfilter enabled', ['0', '1'], 1);
WebsiteSetting::where('key', '=', 'website_wordfilter_enabled')->update([
'value' => $wordfilterEnabled,
]);
$this->progressInfo($step);
$step++;
$requiredBetaCode = $this->choice('Requires beta code to register', ['0', '1'], 0);
WebsiteSetting::where('key', '=', 'requires_beta_code')->update([
'value' => $requiredBetaCode,
]);
$this->progressInfo($step);
$step++;
$registrationDisabled = $this->choice('Disable registration (Can be re-enabled later inside website_settings table if set to 1)', ['0', '1'], 0);
WebsiteSetting::where('key', '=', 'disable_registration')->update([
'value' => $registrationDisabled,
]);
$this->progressInfo($step);
$step++;
$giveHC = $this->choice('Give all new users HC automatically', ['0', '1'], 0);
WebsiteSetting::where('key', '=', 'give_hc_on_register')->update([
'value' => $giveHC,
]);
$this->progressInfo($step);
$maxCommentArticles = $this->ask('Enter the amount of comments each user can post per article (default is 2)');
WebsiteSetting::where('key', '=', 'max_comment_per_article')->update([
'value' => empty($maxCommentArticles) ? '2' : $maxCommentArticles,
]);
}
$seeders = [
'WebsiteLanguageSeeder',
'WebsiteArticleSeeder',
'WebsitePermissionSeeder',
'WebsiteWordfilterSeeder',
'WebsiteTeamSeeder',
'WebsiteRuleCategorySeeder',
'WebsiteRuleSeeder',
];
foreach ($seeders as $seeder) {
Artisan::call(sprintf('db:seed --class=%s', $seeder));
}
$this->info('The setup was successful!');
}
}
@@ -1,88 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\WebsiteAd;
use App\Services\SettingsService;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
class ImportAdsData extends Command
{
protected $signature = 'import:ads-data';
protected $description = 'Import ads data from the filesystem';
private const int CHUNK_SIZE = 100;
private const array ALLOWED_EXTENSIONS = ['jpeg', 'jpg', 'png', 'gif'];
public function handle(SettingsService $settingsService): void
{
$adsPath = $settingsService->getOrDefault('ads_path_filesystem');
if (! $this->validatePath($adsPath)) {
return;
}
$files = $this->getImageFiles($adsPath);
if ($files === []) {
$this->warn('No valid image files found in the ads directory.');
return;
}
$this->processFiles($files);
$this->info('Ads data import completed successfully.');
}
private function validatePath(?string $adsPath): bool
{
if (in_array($adsPath, [null, '', '0'], true)) {
$this->error('Ads path is not configured in website_settings.');
return false;
}
if (! is_dir($adsPath)) {
$this->error("The ads path '{$adsPath}' does not exist in the filesystem.");
return false;
}
return true;
}
private function getImageFiles(string $adsPath): array
{
return array_filter(scandir($adsPath), function ($file) use ($adsPath) {
$filePath = $adsPath . DIRECTORY_SEPARATOR . $file;
return is_file($filePath) &&
in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), self::ALLOWED_EXTENSIONS);
});
}
private function processFiles(array $files): void
{
// Get existing images to avoid duplicates
$existingImages = WebsiteAd::pluck('image')->toArray();
$newFiles = Collection::make($files)
->filter(fn ($file) => ! in_array($file, $existingImages))
->map(fn ($file) => ['image' => $file])
->values();
$skippedCount = count($files) - $newFiles->count();
if ($skippedCount > 0) {
$this->warn("Skipped {$skippedCount} existing files.");
}
$newFiles->chunk(self::CHUNK_SIZE)->each(function ($chunk): void {
WebsiteAd::insert($chunk->toArray());
$this->info('Processed ' . $chunk->count() . ' files.');
});
}
}
@@ -1,94 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\WebsiteBadge;
use App\Services\SettingsService;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
class ImportBadgeData extends Command
{
protected $signature = 'import:badge-data';
protected $description = 'Import badge data from JSON file';
private const int CHUNK_SIZE = 100;
private const string BADGE_PREFIX = 'badge_desc_';
public function __construct(
private readonly SettingsService $settingsService,
) {
parent::__construct();
}
public function handle(): void
{
$jsonPath = $this->settingsService->getOrDefault('nitro_external_texts_file');
if (! $this->validateJsonFile($jsonPath)) {
return;
}
try {
$this->processBadgeData($jsonPath);
$this->info('Badge data imported successfully.');
} catch (Exception $e) {
Log::error('Failed to import badge data: ' . $e->getMessage());
$this->error('Failed to import badge data. Check the logs for details.');
}
}
private function validateJsonFile(?string $jsonPath): bool
{
if (in_array($jsonPath, [null, '', '0'], true)) {
$this->error('The JSON file path is not configured in the website settings.');
return false;
}
if (! file_exists($jsonPath)) {
$this->error('The JSON file does not exist at the specified path: ' . $jsonPath);
return false;
}
return true;
}
private function processBadgeData(string $jsonPath): void
{
$jsonData = File::json($jsonPath);
// Extract badge names and descriptions
$badgeNames = Collection::make($jsonData)
->filter(fn ($value, $key) => str_starts_with((string) $key, 'badge_name_'))
->mapWithKeys(fn ($value, $key) => [str_replace('badge_name_', '', $key) => $value]);
$badgeDescriptions = Collection::make($jsonData)
->filter(fn ($value, $key) => str_starts_with((string) $key, self::BADGE_PREFIX))
->mapWithKeys(fn ($value, $key) => [str_replace(self::BADGE_PREFIX, '', $key) => $value]);
// Combine badge names and descriptions
$badgeData = $badgeNames->map(fn ($name, $key) => [
'badge_key' => $key, // Use only the badge name (e.g., 14X12, 14XR1)
'badge_name' => $name,
'badge_description' => $badgeDescriptions->get($key, 'No description available'),
])->values();
// Upsert the combined data in chunks
$badgeData->chunk(self::CHUNK_SIZE)->each(function ($chunk): void {
WebsiteBadge::upsert(
$chunk->toArray(),
['badge_key'],
['badge_name', 'badge_description'],
);
$this->info('Processed ' . $chunk->count() . ' badges.');
});
}
}
-29
View File
@@ -1,29 +0,0 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
#[\Override]
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*/
#[\Override]
protected function commands(): void
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}
@@ -1,31 +0,0 @@
<?php
namespace App\Enums;
enum AchievementCategory: string
{
case Identity = 'identity';
case Explore = 'explore';
case Music = 'music';
case Social = 'social';
case Games = 'games';
case RoomBuilder = 'room_builder';
case Pets = 'pets';
case Tools = 'tools';
case Events = 'events';
public static function values(): array
{
return array_column(self::cases(), 'value');
}
public static function toInput(): array
{
$allCurrencies = self::cases();
return array_combine(
array_column($allCurrencies, 'value'),
array_column($allCurrencies, 'name'),
);
}
}
-49
View File
@@ -1,49 +0,0 @@
<?php
namespace App\Enums;
enum CurrencyTypes: int
{
case Credits = -1;
case Duckets = 0;
case Diamonds = 5;
case Points = 101;
public static function values(): array
{
return array_column(self::cases(), 'value');
}
public static function fromCurrencyName(string $currencyName): ?self
{
$currencyName = strtolower($currencyName);
return match ($currencyName) {
'credits' => self::Credits,
'duckets' => self::Duckets,
'diamonds' => self::Diamonds,
'points' => self::Points,
default => null,
};
}
public function getImage(): string
{
return match ($this->value) {
CurrencyTypes::Credits->value => asset('assets/images/currencies/credits.gif'),
CurrencyTypes::Duckets->value => asset('assets/images/currencies/duckets.png'),
CurrencyTypes::Diamonds->value => asset('assets/images/currencies/diamonds.png'),
CurrencyTypes::Points->value => asset('assets/images/currencies/points.png'),
};
}
public static function toInput(): array
{
$allCurrencies = self::cases();
return array_combine(
array_column($allCurrencies, 'value'),
array_column($allCurrencies, 'name'),
);
}
}
-49
View File
@@ -1,49 +0,0 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
#[\Override]
public function register(): void
{
$this->reportable(function (Throwable $e): void {
//
});
}
}
@@ -1,7 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class MigrationFailedException extends Exception {}
@@ -1,7 +0,0 @@
<?php
namespace App\Exceptions;
use Exception;
class RconConnectionException extends Exception {}
@@ -1,31 +0,0 @@
<?php
namespace App\Filament\Filters;
use Filament\Forms\Components\DatePicker;
use Filament\Tables\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;
class DateRangeFilter extends Filter
{
#[\Override]
public static function make(?string $name = null): static
{
return parent::make($name)
->schema([
DatePicker::make("{$name}_from"),
DatePicker::make("{$name}_until"),
])
->query(function (Builder $query, array $data) use (&$name): Builder {
return $query
->when(
$data["{$name}_from"],
fn (Builder $query, $date): Builder => $query->whereDate($name, '>=', $date),
)
->when(
$data["{$name}_until"],
fn (Builder $query, $date): Builder => $query->whereDate($name, '<=', $date),
);
});
}
}
@@ -1,315 +0,0 @@
<?php
namespace App\Filament\Pages;
use App\Filament\Traits\TranslatableResource;
use App\Services\Parsers\ExternalTextsParser;
use Filament\Actions\Action as PageAction;
use Filament\Actions\ActionGroup;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Schema;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;
class BadgePage extends Page
{
use InteractsWithForms, TranslatableResource;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-document-text';
protected static string|\UnitEnum|null $navigationGroup = 'Hotel';
protected string $view = 'filament.pages.badge-page';
protected static string $translateIdentifier = 'badge-resource';
public $badgeWasPreviouslyCreated;
public ?array $data = [];
public static string $roleName = 'badge_page';
public static function canAccess(): bool
{
return auth()->user()->can('view::admin::' . static::$roleName);
}
#[\Override]
public function getTitle(): string|Htmlable
{
return __(
sprintf('filament::resources.resources.%s.navigation_label', static::$translateIdentifier),
);
}
public function form(Schema $schema): Schema
{
return $schema
->components([
Section::make(__('filament::resources.tabs.Main'))
->schema([
TextInput::make('code')
->label(__('filament::resources.inputs.badge_code'))
->helperText(__('filament::resources.helpers.badge_code_helper'))
->afterStateUpdated(function (?string $state, Set $set): void {
$set('code', strtoupper($state));
})
->suffixAction(fn (): PageAction => PageAction::make('search')->icon('heroicon-o-magnifying-glass')->action(fn () => $this->searchBadgesByCode()),
),
TextInput::make('image')
->label(__('filament::resources.inputs.badge_image'))
->placeholder('...')
->autocomplete()
->visible(fn (Get $get) => isset($this->data['image']) ?? false)
->prefixAction(
fn (?string $state): PageAction => PageAction::make('visit')
->icon('heroicon-s-arrow-top-right-on-square')
->tooltip(__('filament::resources.common.Open link'))
->url($state)
->visible(fn () => ! in_array($state, [null, '', '0'], true))
->openUrlInNewTab(),
),
]),
Section::make('Nitro Texts')
->collapsible()
->visible(fn () => isset($this->data['nitro']) && ! empty($this->data['nitro']))
->schema([
TextInput::make('nitro.title')
->label(__('filament::resources.inputs.badge_title'))
->placeholder('...')
->visible(fn () => isset($this->data['nitro']['title']) ?? false),
TextInput::make('nitro.description')
->label(__('filament::resources.inputs.badge_description'))
->placeholder('...')
->visible(fn () => isset($this->data['nitro']['description']) ?? false),
]),
Section::make('Flash Texts')
->collapsible()
->visible(fn () => isset($this->data['flash']) && ! empty($this->data['flash']))
->schema([
TextInput::make('flash.title')
->label(__('filament::resources.inputs.badge_title'))
->placeholder('...')
->visible(fn () => isset($this->data['flash']['title']) ?? false),
TextInput::make('flash.description')
->label(__('filament::resources.inputs.badge_description'))
->placeholder('...')
->visible(fn () => isset($this->data['flash']['description']) ?? false),
]),
])
->statePath('data');
}
private function searchBadgesByCode(): void
{
$badgeCode = $this->form->getState()['code'] ?? null;
if (empty($badgeCode)) {
$this->notify('danger', __('filament::resources.notifications.badge_code_required'));
return;
}
$badgeData = app(ExternalTextsParser::class)->getBadgeData($badgeCode);
$this->badgeWasPreviouslyCreated = is_array($badgeData['nitro']) || is_array($badgeData['flash']);
if ($this->badgeWasPreviouslyCreated) {
Notification::make()
->icon('heroicon-o-check-circle')
->iconColor('success')
->color('success')
->title(__('filament::resources.notifications.badge_found'))
->send();
$this->data = [
'code' => $badgeCode,
...$this->getDefaultDataBehavior(
$badgeData['image'] ?? null,
$badgeData['nitro']['title'] ?? null,
$badgeData['nitro']['description'] ?? null,
$badgeData['flash']['title'] ?? null,
$badgeData['flash']['description'] ?? null,
),
];
return;
}
Notification::make()
->color('success')
->icon('heroicon-o-check-circle')
->iconColor('success')
->title(__('filament::resources.notifications.create_badge'))
->send();
$this->data = [
'code' => $badgeCode,
...$this->getDefaultDataBehavior(),
];
}
private function getDefaultDataBehavior(
?string $badgeImageUrl = null,
?string $nitroTitle = null,
?string $nitroDesc = null,
?string $flashTitle = null,
?string $flashDesc = null,
): array {
return [
'image' => $badgeImageUrl ?? '',
'nitro' => [
'title' => $nitroTitle ?? '',
'description' => $nitroDesc ?? '',
],
'flash' => [
'title' => $flashTitle ?? '',
'description' => $flashDesc ?? '',
],
];
}
public function create()
{
$nitroEnabled = config('hotel.client.nitro.enabled');
$flashEnabled = config('hotel.client.flash.enabled');
// image and code fields are required when creating a new badge
if (! $this->badgeWasPreviouslyCreated && (empty($this->data['image']) || empty($this->data['code']))) {
$notificationTitle = empty($this->data['image']) ?
__('filament::resources.notifications.badge_image_required') :
__('filament::resources.notifications.badge_code_required');
Notification::make()
->icon('heroicon-o-exclamation-triangle')
->iconColor('danger')
->color('danger')
->title($notificationTitle)
->send();
return;
}
$externalTextsParser = app(ExternalTextsParser::class);
if ((empty($this->data['nitro']) && $nitroEnabled) || (empty($this->data['flash']) && $flashEnabled)) {
Notification::make()
->icon('heroicon-o-exclamation-triangle')
->iconColor('danger')
->color('danger')
->title(__('filament::resources.notifications.badge_texts_required'))
->send();
return;
}
try {
$this->uploadBadgeImage($externalTextsParser);
if (! empty($this->data['nitro']) && $nitroEnabled) {
$externalTextsParser->updateNitroBadgeTexts($this->data['code'], ...$this->data['nitro']);
}
if (! empty($this->data['flash']) && $flashEnabled) {
$externalTextsParser->updateFlashBadgeTexts($this->data['code'], ...$this->data['flash']);
}
} catch (Throwable $exception) {
Log::channel('badge')->error('[ORION BADGE RESOURCE] - ERROR: ' . $exception->getMessage());
Notification::make()
->icon('heroicon-o-exclamation-triangle')
->iconColor('danger')
->color('danger')
->title(__('filament::resources.notifications.badge_update_failed'))
->send();
return;
}
$this->data['image'] = $externalTextsParser->getBadgeImageUrl($this->data['code']);
$this->badgeWasPreviouslyCreated = true;
Notification::make()
->icon('heroicon-o-check-circle')
->iconColor('success')
->color('success')
->title(__('filament::resources.notifications.badge_updated'))
->send();
}
protected function uploadBadgeImage(ExternalTextsParser $parser): void
{
if (empty($this->data['image']) || ! filter_var($this->data['image'], FILTER_VALIDATE_URL)) {
return;
}
if ($this->data['image'] == $parser->getBadgeImageUrl($this->data['code'])) {
return;
}
$image = Http::get($this->data['image']);
if (! $image->successful()) {
return;
}
$contentType = $image->header('content-type');
$gdImage = match ($contentType) {
'image/png' => imagecreatefrompng($this->data['image']),
'image/gif' => imagecreatefromgif($this->data['image']),
'image/jpeg' => imagecreatefromjpeg($this->data['image']),
default => false
};
if ($gdImage === false) {
Notification::make()
->icon('heroicon-o-exclamation-triangle')
->iconColor('danger')
->color('danger')
->title(__('filament::resources.notifications.badge_image_upload_failed'))
->send();
return;
}
$uploadPath = public_path(sprintf('%s%s%s.gif',
rtrim((string) config('hotel.client.flash.relative_files_path'), '\//'),
'/c_images/album1584/',
$this->data['code'],
));
imagegif($gdImage, $uploadPath);
}
/**
* @return array<\Filament\Actions\Action|ActionGroup>
*/
protected function getHeaderActions(): array
{
return [
PageAction::make('save')
->label(__('filament::resources.common.Update'))
->action(fn () => $this->create())
->color('primary')
->visible(fn () => isset($this->data['code']) && $this->badgeWasPreviouslyCreated),
PageAction::make('create')
->label(__('filament::resources.common.Create'))
->action(fn () => $this->create())
->color('success')
->visible(fn () => isset($this->data['code']) && ! $this->badgeWasPreviouslyCreated),
];
}
}
@@ -1,26 +0,0 @@
<?php
namespace App\Filament\Pages;
use App\Filament\Traits\TranslatableResource;
use Filament\Pages\Dashboard as FilamentDashboard;
class Dashboard extends FilamentDashboard
{
use TranslatableResource;
protected static string|\UnitEnum|null $navigationGroup = 'Dashboard';
protected static ?string $navigationLabel = 'Homepage';
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-home';
public static string $translateIdentifier = 'dashboard';
public static string $roleName = 'dashboard';
public static function canAccess(): bool
{
return auth()->user()->can('view::admin::' . static::$roleName);
}
}
-109
View File
@@ -1,109 +0,0 @@
<?php
namespace App\Filament\Pages;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use Filament\Auth\Http\Responses\Contracts\LoginResponse;
use Filament\Facades\Filament;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\TextInput;
use Filament\Models\Contracts\FilamentUser;
use Filament\Notifications\Notification;
use Filament\Schemas\Components\Component;
use Illuminate\Validation\ValidationException;
class Login extends \Filament\Auth\Pages\Login
{
public $username = '';
#[\Override]
public function authenticate(): ?LoginResponse
{
try {
$this->rateLimit(5);
} catch (TooManyRequestsException $exception) {
Notification::make()
->title(__('filament-panels::pages/auth/login.notifications.throttled.title', [
'seconds' => $exception->secondsUntilAvailable,
'minutes' => ceil($exception->secondsUntilAvailable / 60),
]))
->body(array_key_exists('body', __('filament-panels::pages/auth/login.notifications.throttled') ?: []) ? __('filament-panels::pages/auth/login.notifications.throttled.body', [
'seconds' => $exception->secondsUntilAvailable,
'minutes' => ceil($exception->secondsUntilAvailable / 60),
]) : null)
->danger()
->send();
return null;
}
$data = $this->form->getState();
if (! Filament::auth()->attempt($this->getCredentialsFromFormData($data), $data['remember'] ?? false)) {
$this->throwFailureValidationException();
}
$user = Filament::auth()->user();
if (
($user instanceof FilamentUser) &&
(! $user->canAccessPanel(Filament::getCurrentOrDefaultPanel()))
) {
Filament::auth()->logout();
$this->throwFailureValidationException();
}
session()->regenerate();
return app(LoginResponse::class);
}
protected function throwFailureValidationException(): never
{
throw ValidationException::withMessages([
'data.username' => __('filament-panels::pages/auth/login.messages.failed'),
]);
}
protected function getFormSchema(): array
{
return [
TextInput::make('username')
->label(__('filament::login.fields.username.label'))
->required()
->autocomplete(),
TextInput::make('password')
->label(__('filament::login.fields.password.label'))
->password()
->required(),
Checkbox::make('remember')
->label(__('filament::login.fields.remember.label')),
];
}
#[\Override]
protected function getEmailFormComponent(): Component
{
return TextInput::make('username')
->label(__('filament::login.fields.username.label'))
->required()
->autocomplete()
->autofocus()
->extraInputAttributes(['tabindex' => 1]);
}
/**
* @param array<string, mixed> $data
*
* @return array<string, mixed>
*/
#[\Override]
protected function getCredentialsFromFormData(array $data): array
{
return [
'username' => $data['username'],
'password' => $data['password'],
];
}
}
@@ -1,227 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Articles;
use App\Filament\Resources\Atom\Articles\Pages\CreateArticle;
use App\Filament\Resources\Atom\Articles\Pages\EditArticle;
use App\Filament\Resources\Atom\Articles\Pages\ListArticles;
use App\Filament\Resources\Atom\Articles\Pages\ViewArticle;
use App\Filament\Resources\Atom\Articles\RelationManagers\TagsRelationManager;
use App\Filament\Traits\TranslatableResource;
use App\Models\Articles\WebsiteArticle;
use Exception;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class ArticleResource extends Resource
{
use TranslatableResource;
protected static ?string $model = WebsiteArticle::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-newspaper';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'website/articles';
public static string $translateIdentifier = 'articles';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components(static::getForm());
}
public static function getForm(): array
{
return [
Tabs::make('Main')
->tabs([
Tab::make(__('filament::resources.tabs.Home'))
->icon('heroicon-o-home')
->schema([
TextInput::make('title')
->label(__('filament::resources.inputs.title'))
->required()
->autocomplete()
->maxLength(255)
->columnSpan('full'),
TextInput::make('short_story')
->label(__('filament::resources.inputs.description'))
->required()
->maxLength(255)
->autocomplete()
->columnSpan('full'),
FileUpload::make('image')
->label(__('filament::resources.inputs.image'))
->directory('website_news_images')
->visibility('public'),
RichEditor::make('full_story')
->label(__('filament::resources.inputs.content'))
->required()
->columnSpan('full'),
Hidden::make('user_id')
->default(fn () => auth()->check() ? auth()->user()->id : null),
]),
Tab::make(__('filament::resources.tabs.Configurations'))
->icon('heroicon-o-cog')
->schema([
Toggle::make('is_visible')
->label(__('filament::resources.inputs.visible'))
->onIcon('heroicon-s-check')
->offIcon('heroicon-s-x-mark')
->default(true)
->live()
->afterStateUpdated(function (string $operation, $state, $record): void {
if ($operation !== 'edit' || is_null($record)) {
return;
}
try {
if ($state) {
$record->restore();
} else {
$record->delete();
}
} catch (Exception $e) {
report($e);
}
})
->formatStateUsing(function ($record) {
if (is_null($record)) {
return true;
}
return is_null($record->deleted_at);
}),
Toggle::make('can_comment')
->onIcon('heroicon-s-check')
->label(__('filament::resources.inputs.allow_comments'))
->default(true)
->offIcon('heroicon-s-x-mark'),
]),
])->columnSpanFull(),
];
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->poll('60s')
->columns(static::getTable())
->filters([
TrashedFilter::make(),
])
->recordActions([
ViewAction::make(),
EditAction::make(),
DeleteAction::make(),
RestoreAction::make(),
ForceDeleteAction::make(),
])
->toolbarActions([
DeleteBulkAction::make(),
RestoreBulkAction::make(),
ForceDeleteBulkAction::make(),
]);
}
public static function getTable(): array
{
return [
TextColumn::make('id')
->label(__('filament::resources.columns.id')),
ImageColumn::make('image')
->circular()
->extraAttributes(['style' => 'image-rendering: pixelated'])
->size(50)
->label(__('filament::resources.columns.image')),
TextColumn::make('title')
->label(__('filament::resources.columns.title'))
->searchable()
->limit(50),
TextColumn::make('user.username')
->searchable()
->label(__('filament::resources.columns.by')),
ToggleColumn::make('is_visible')
->label(__('filament::resources.columns.visible'))
->onIcon('heroicon-s-check')
->toggleable()
->state(fn ($record) => is_null($record->deleted_at))
->disabled(),
ToggleColumn::make('allow_comments')
->label(__('filament::resources.columns.allow_comments'))
->onIcon('heroicon-s-check')
->toggleable()
->disabled(),
];
}
#[\Override]
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
#[\Override]
public static function getRelations(): array
{
return [
TagsRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => ListArticles::route('/'),
'create' => CreateArticle::route('/create'),
'view' => ViewArticle::route('/{record}'),
'edit' => EditArticle::route('/{record}/edit'),
];
}
public static function getGlobalSearchEloquentQuery(): Builder
{
return parent::getGlobalSearchEloquentQuery()->withTrashed();
}
}
@@ -1,24 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Articles\Pages;
use App\Filament\Resources\Atom\Articles\ArticleResource;
use App\Models\Article;
use Filament\Resources\Pages\CreateRecord;
class CreateArticle extends CreateRecord
{
protected static string $resource = ArticleResource::class;
protected function afterCreate(): void
{
/** @var null|Article $articleCreated */
$articleCreated = $this->getRecord();
if (! $articleCreated || ! $articleCreated->visible) {
return;
}
$articleCreated->createFollowersNotification();
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Articles\Pages;
use App\Filament\Resources\Atom\Articles\ArticleResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditArticle extends EditRecord
{
protected static string $resource = ArticleResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Articles\Pages;
use App\Filament\Resources\Atom\Articles\ArticleResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListArticles extends ListRecords
{
protected static string $resource = ArticleResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,31 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Articles\Pages;
use App\Filament\Resources\Atom\Articles\ArticleResource;
use Filament\Actions\Action;
use Filament\Actions\EditAction;
use Filament\Resources\Pages\ViewRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
class ViewArticle extends ViewRecord
{
protected static string $resource = ArticleResource::class;
public function getHeaderActions(): array
{
return [
Action::make('Send Notification')
->label(__('Send notifications'))
->color('gray')
->visible(fn (Model $record) => $record->user_id === Auth::id())
->requiresConfirmation()
->action(function (Model $record): void {
$record->createFollowersNotification();
}),
EditAction::make(),
];
}
}
@@ -1,59 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Articles\RelationManagers;
use App\Filament\Resources\Atom\Tags\TagResource;
use App\Filament\Traits\TranslatableResource;
use Filament\Actions\AttachAction;
use Filament\Actions\CreateAction;
use Filament\Actions\DetachAction;
use Filament\Actions\DetachBulkAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\TextInput;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
class TagsRelationManager extends RelationManager
{
use TranslatableResource;
protected static string $relationship = 'tags';
protected static ?string $recordTitleAttribute = 'name';
public static string $translateIdentifier = 'tags';
public function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->required()
->maxLength(255),
]);
}
public function table(Table $table): Table
{
return $table
->columns(TagResource::getTable())
->modifyQueryUsing(fn ($query) => $query->latest())
->filters([
//
])
->headerActions([
CreateAction::make()
->schema(TagResource::getForm()),
AttachAction::make()->preloadRecordSelect(),
])
->recordActions([
ViewAction::make(),
DetachAction::make(),
])
->toolbarActions([
DetachBulkAction::make(),
]);
}
}
@@ -1,93 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\CameraWebs;
use App\Filament\Resources\Atom\CameraWebs\Pages\EditCameraWeb;
use App\Filament\Resources\Atom\CameraWebs\Pages\ListCameraWeb;
use App\Models\Miscellaneous\CameraWeb;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Table;
class CameraWebResource extends Resource
{
protected static ?string $model = CameraWeb::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-photo';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'camera-web';
protected static ?string $pluralModelLabel = 'photos';
protected static ?string $navigationLabel = 'Web Camera';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
Toggle::make('visible')
->label(__('Visible'))
->default(true),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
TextColumn::make('id')
->label(__('filament::resources.columns.id'))
->sortable(),
TextColumn::make('user_id')
->label(__('filament::resources.columns.user_id')),
TextColumn::make('room_id')
->label(__('filament::resources.columns.room_id')),
TextColumn::make('timestamp')
->label(__('filament::resources.columns.created_at'))
->dateTime(),
ImageColumn::make('url')
->label(__('filament::resources.columns.image'))
->extraAttributes(['style' => 'image-rendering: pixelated'])
->size(125),
ToggleColumn::make('visible')
->label(__('Visible')),
])
->recordActions([
DeleteAction::make(),
])
->toolbarActions([
DeleteBulkAction::make(),
]);
}
#[\Override]
public static function getRelations(): array
{
return [
];
}
public static function getPages(): array
{
return [
'index' => ListCameraWeb::route('/'),
'edit' => EditCameraWeb::route('/{record}/edit'),
];
}
public static function canCreate(): bool
{
return false;
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\CameraWebs\Pages;
use App\Filament\Resources\Atom\CameraWebs\CameraWebResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditCameraWeb extends EditRecord
{
protected static string $resource = CameraWebResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\CameraWebs\Pages;
use App\Filament\Resources\Atom\CameraWebs\CameraWebResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListCameraWeb extends ListRecords
{
protected static string $resource = CameraWebResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,112 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\CmsSettings;
use App\Filament\Resources\Atom\CmsSettings\Pages\ManageCmsSettings;
use App\Filament\Traits\TranslatableResource;
use App\Models\Miscellaneous\WebsiteSetting;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class CmsSettingResource extends Resource
{
use TranslatableResource;
protected static ?string $model = WebsiteSetting::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-cpu-chip';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'website/cms-settings';
public static string $translateIdentifier = 'cms-settings';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
TextInput::make('key')
->label(__('filament::resources.inputs.key'))
->maxLength(50)
->autocomplete()
->unique(ignoreRecord: true)
->required(),
TextInput::make('value')
->label(__('filament::resources.inputs.value'))
->required()
->maxLength(255)
->autocomplete(),
TextInput::make('comment')
->label(__('filament::resources.inputs.comment'))
->nullable()
->maxLength(255)
->autocomplete()
->columnSpanFull(),
])
->columns([
'sm' => 2,
]),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
TextColumn::make('key')
->label(__('filament::resources.columns.key'))
->searchable(),
TextColumn::make('value')
->label(__('filament::resources.columns.value'))
->searchable()
->limit(30),
TextColumn::make('comment')
->label(__('filament::resources.columns.comment'))
->toggleable()
->searchable()
->tooltip(function (TextColumn $column): ?string {
$state = $column->getState();
if (strlen($state) <= $column->getCharacterLimit()) {
return null;
}
return $state;
})
->limit(60),
])
->filters([
//
])
->recordActions([
EditAction::make(),
DeleteAction::make(),
])
->toolbarActions([
// ...
]);
}
public static function getPages(): array
{
return [
'index' => ManageCmsSettings::route('/'),
];
}
}
@@ -1,24 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\CmsSettings\Pages;
use App\Filament\Resources\Atom\CmsSettings\CmsSettingResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ManageRecords;
class ManageCmsSettings extends ManageRecords
{
protected static string $resource = CmsSettingResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
protected function getTableRecordsPerPageSelectOptions(): array
{
return [25, 50, 100];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionCategoryResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionCategoryResource;
use Filament\Resources\Pages\CreateRecord;
class CreateHelpQuestionCategory extends CreateRecord
{
protected static string $resource = HelpQuestionCategoryResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionCategoryResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionCategoryResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditHelpQuestionCategory extends EditRecord
{
protected static string $resource = HelpQuestionCategoryResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,24 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionCategoryResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionCategoryResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListHelpQuestionCategories extends ListRecords
{
protected static string $resource = HelpQuestionCategoryResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
protected function getTableReorderColumn(): ?string
{
return 'order';
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionCategoryResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionCategoryResource;
use Filament\Resources\Pages\ViewRecord;
class ViewHelpQuestionCategory extends ViewRecord
{
protected static string $resource = HelpQuestionCategoryResource::class;
}
@@ -1,48 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionCategoryResource\RelationManagers;
use App\Filament\Resources\Atom\HelpQuestionResource;
use App\Filament\Traits\TranslatableResource;
use Filament\Actions\AttachAction;
use Filament\Actions\DetachAction;
use Filament\Actions\DetachBulkAction;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
class QuestionsRelationManager extends RelationManager
{
use TranslatableResource;
protected static string $relationship = 'questions';
protected static ?string $recordTitleAttribute = 'title';
public static string $translateIdentifier = 'help-questions';
protected static ?string $inverseRelationship = 'categories';
public function form(Schema $schema): Schema
{
return $schema->components(HelpQuestionResource::getForm(true));
}
public function table(Table $table): Table
{
return $table->columns(HelpQuestionResource::getTable())
->modifyQueryUsing(fn ($query) => $query->latest())
->filters([
//
])
->headerActions([
AttachAction::make(),
])
->recordActions([
DetachAction::make(),
])
->toolbarActions([
DetachBulkAction::make(),
]);
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionResource;
use Filament\Resources\Pages\CreateRecord;
class CreateHelpQuestion extends CreateRecord
{
protected static string $resource = HelpQuestionResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditHelpQuestion extends EditRecord
{
protected static string $resource = HelpQuestionResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListHelpQuestions extends ListRecords
{
protected static string $resource = HelpQuestionResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionResource\Pages;
use App\Filament\Resources\Atom\HelpQuestionResource;
use Filament\Resources\Pages\ViewRecord;
class ViewHelpQuestion extends ViewRecord
{
protected static string $resource = HelpQuestionResource::class;
}
@@ -1,52 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HelpQuestionResource\RelationManagers;
use App\Filament\Resources\Atom\HelpQuestionCategoryResource;
use App\Filament\Traits\TranslatableResource;
use Filament\Actions\AttachAction;
use Filament\Actions\CreateAction;
use Filament\Actions\DetachAction;
use Filament\Actions\DetachBulkAction;
use Filament\Actions\EditAction;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
class CategoriesRelationManager extends RelationManager
{
use TranslatableResource;
protected static string $relationship = 'categories';
protected static ?string $recordTitleAttribute = 'name';
public static string $translateIdentifier = 'help-question-categories';
protected static ?string $inverseRelationship = 'questions';
public function form(Schema $schema): Schema
{
return $schema->components(HelpQuestionCategoryResource::getForm());
}
public function table(Table $table): Table
{
return $table->columns(HelpQuestionCategoryResource::getTable())
->modifyQueryUsing(fn ($query) => $query->latest('id'))
->filters([
//
])
->headerActions([
CreateAction::make(),
AttachAction::make(),
])
->recordActions([
EditAction::make(),
DetachAction::make(),
])
->toolbarActions([
DetachBulkAction::make(),
]);
}
}
@@ -1,119 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HousekeepingPermissions;
use App\Filament\Resources\Atom\HousekeepingPermissions\Pages\ListHousekeepingPermissions;
use App\Models\WebsiteHousekeepingPermission;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class HousekeepingPermissionResource extends Resource
{
protected static ?string $model = WebsiteHousekeepingPermission::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'website/housekeeping-permissions';
protected static ?string $navigationLabel = 'Housekeeping permissions';
public static string $translateIdentifier = 'housekeeping-permissions';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
TextInput::make('permission')
->label(__('filament::resources.inputs.permission'))
->maxLength(50)
->autocomplete()
->unique(ignoreRecord: true)
->required(),
TextInput::make('min_rank')
->label(__('filament::resources.inputs.min_rank'))
->required()
->maxLength(255)
->autocomplete(),
TextInput::make('description')
->label(__('filament::resources.inputs.description'))
->nullable()
->maxLength(255)
->autocomplete()
->columnSpanFull(),
])
->columns([
'sm' => 2,
]),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'asc')
->columns([
TextColumn::make('permission')
->label(__('filament::resources.columns.permission'))
->searchable(),
TextColumn::make('min_rank')
->label(__('filament::resources.columns.min_rank'))
->searchable()
->limit(30),
TextColumn::make('description')
->label(__('filament::resources.columns.description'))
->toggleable()
->searchable()
->tooltip(function (TextColumn $column): ?string {
$state = $column->getState();
if (strlen($state) <= $column->getCharacterLimit()) {
return null;
}
return $state;
})
->limit(60),
])
->filters([
//
])
->recordActions([
EditAction::make(),
DeleteAction::make(),
])
->toolbarActions([
//
]);
}
#[\Override]
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListHousekeepingPermissions::route('/'),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\HousekeepingPermissions\Pages;
use App\Filament\Resources\Atom\HousekeepingPermissions\HousekeepingPermissionResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListHousekeepingPermissions extends ListRecords
{
protected static string $resource = HousekeepingPermissionResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\NavigationResource\Pages;
use App\Filament\Resources\Atom\NavigationResource;
use Filament\Resources\Pages\CreateRecord;
class CreateNavigation extends CreateRecord
{
protected static string $resource = NavigationResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\NavigationResource\Pages;
use App\Filament\Resources\Atom\NavigationResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditNavigation extends EditRecord
{
protected static string $resource = NavigationResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\NavigationResource\Pages;
use App\Filament\Resources\Atom\NavigationResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListNavigations extends ListRecords
{
protected static string $resource = NavigationResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,92 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\NavigationResource\RelationManagers;
use Filament\Actions\CreateAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\DissociateAction;
use Filament\Actions\DissociateBulkAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Table;
class SubNavigationsRelationManager extends RelationManager
{
protected static string $relationship = 'subNavigations';
protected static ?string $recordTitleAttribute = 'label';
public function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('label')
->label(__('filament::resources.inputs.label'))
->columnSpanFull()
->required()
->maxLength(255),
TextInput::make('slug')
->label(__('filament::resources.inputs.slug')),
TextInput::make('order')
->numeric()
->minValue(0)
->default(0)
->label(__('filament::resources.columns.order')),
Toggle::make('visible')
->label(__('filament::resources.columns.visible')),
Toggle::make('new_tab')
->label(__('filament::resources.columns.new_tab')),
])
->columns([
'sm' => 2,
]);
}
public function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('label'),
TextColumn::make('slug')
->label(__('filament::resources.columns.slug')),
ToggleColumn::make('visible')
->label(__('filament::resources.columns.visible')),
ToggleColumn::make('new_tab')
->label(__('filament::resources.columns.new_tab')),
TextColumn::make('order')
->label(__('filament::resources.columns.order')),
])
->reorderable('order')
->filters([
//
])
->headerActions([
CreateAction::make(),
// Tables\Actions\AssociateAction::make(),
])
->recordActions([
EditAction::make(),
DissociateAction::make(),
DeleteAction::make(),
])
->toolbarActions([
DissociateBulkAction::make(),
DeleteBulkAction::make(),
]);
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Permissions\Pages;
use App\Filament\Resources\Atom\Permissions\PermissionResource;
use Filament\Resources\Pages\CreateRecord;
class CreatePermission extends CreateRecord
{
protected static string $resource = PermissionResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Permissions\Pages;
use App\Filament\Resources\Atom\Permissions\PermissionResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditPermission extends EditRecord
{
protected static string $resource = PermissionResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Permissions\Pages;
use App\Filament\Resources\Atom\Permissions\PermissionResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListPermissions extends ListRecords
{
protected static string $resource = PermissionResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Permissions\Pages;
use App\Filament\Resources\Atom\Permissions\PermissionResource;
use Filament\Actions\EditAction;
use Filament\Resources\Pages\ViewRecord;
class ViewPermission extends ViewRecord
{
protected static string $resource = PermissionResource::class;
public function getHeaderActions(): array
{
return [
EditAction::make(),
];
}
}
@@ -1,268 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Permissions;
use App\Filament\Resources\Atom\Permissions\Pages\CreatePermission;
use App\Filament\Resources\Atom\Permissions\Pages\EditPermission;
use App\Filament\Resources\Atom\Permissions\Pages\ListPermissions;
use App\Filament\Resources\Atom\Permissions\Pages\ViewPermission;
use App\Filament\Tables\Columns\HabboBadgeColumn;
use App\Filament\Traits\TranslatableResource;
use App\Models\Game\Permission;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\ColorPicker;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\ToggleButtons;
use Filament\Pages\Enums\SubNavigationPosition;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\HtmlString;
use Str;
class PermissionResource extends Resource
{
use TranslatableResource;
protected static ?string $model = Permission::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'website/permissions';
public static string $translateIdentifier = 'permissions';
protected static ?string $recordTitleAttribute = 'rank_name';
protected static ?\Filament\Pages\Enums\SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
#[\Override]
public static function form(\Filament\Schemas\Schema $schema): \Filament\Schemas\Schema
{
/**
* @param string $name
* @param bool $needsSecondOption = false
*/
$groupedToggleButton = fn (string $name, bool $needsSecondOption = false): ToggleButtons => ToggleButtons::make($name)
->label(function () use ($name) {
$translationKey = "filament::resources.permissions.{$name}";
$translation = __($translationKey);
if ($translationKey == $translation) {
return $name;
}
return $translation;
})
->options(function () use ($needsSecondOption) {
$options = [
'0' => __('filament::resources.options.no'),
'1' => __('filament::resources.options.yes'),
];
if ($needsSecondOption) {
$options['2'] = __('filament::resources.options.rights');
}
return $options;
})
->icons(['0' => 'heroicon-o-check', '1' => 'heroicon-o-x-mark', '2' => 'heroicon-o-sparkles'])
->colors(['0' => 'danger', '1' => 'success'])
->grouped();
return $schema
->components([
Tabs::make('Main')
->tabs([
Tab::make(__('filament::resources.tabs.General Information'))
->schema([
TextInput::make('rank_name')
->label(__('filament::resources.inputs.name'))
->maxLength(25)
->required(),
TextInput::make('badge')
->label(__('filament::resources.inputs.badge_code'))
->maxLength(12)
->required(),
TextInput::make('level')
->label(__('filament::resources.inputs.level'))
->required(),
TextInput::make('room_effect')
->label(__('filament::resources.inputs.room_effect'))
->required(),
]),
Tab::make(__('filament::resources.tabs.In-game Permissions'))
->schema([
Section::make(__('filament::resources.sections.permissions.title'))
->description(new HtmlString(__('filament::resources.sections.permissions.description')))
->schema([
Grid::make()
->columns([
'sm' => 2,
'md' => 3,
'lg' => 3,
])
->schema(function () use ($groupedToggleButton) {
$columns = Schema::getColumns('permissions');
$arcturusPermissions = collect($columns)->filter(function (array $column) {
$columnName = $column['name'] ?? null;
if (! $columnName) {
return false;
}
return str_starts_with($columnName, 'cmd')
|| str_starts_with($columnName, 'acc')
|| str_ends_with($columnName, 'cmd');
})->values();
return $arcturusPermissions->map(function (array $column) use ($groupedToggleButton) {
$columnName = $column['name'];
$needsSecondOption = $column['type_name'] == 'enum' && str_ends_with((string) $column['type'], "'2')");
return $groupedToggleButton($columnName, $needsSecondOption);
})->toArray();
}),
]),
]),
Tab::make(__('filament::resources.tabs.Configurations'))
->schema([
Grid::make(['default' => 2])
->schema([
Select::make('log_commands')
->label(__('filament::resources.inputs.log_commands'))
->columnSpanFull()
->options([
'0' => __('filament::resources.options.no'),
'1' => __('filament::resources.options.yes'),
]),
TextInput::make('prefix')
->label(__('filament::resources.inputs.prefix'))
->maxLength(5)
->required(),
ColorPicker::make('prefix_color')
->label(__('filament::resources.inputs.prefix_color'))
->required(),
Toggle::make('hidden_rank')
->label(__('filament::resources.inputs.is_hidden'))
->columnSpanFull(),
Section::make()
->schema([
Grid::make()
->columns([
'md' => 2,
])
->schema([
TextInput::make('auto_credits_amount')
->columnSpan(1)
->label(__('filament::resources.inputs.auto_credits_amount'))
->required(),
TextInput::make('auto_pixels_amount')
->label(__('filament::resources.inputs.auto_pixels_amount'))
->required(),
TextInput::make('auto_gotw_amount')
->label(__('filament::resources.inputs.auto_gotw_amount'))
->required(),
TextInput::make('auto_points_amount')
->label(__('filament::resources.inputs.auto_points_amount'))
->required(),
]),
]),
]),
]),
])
->columnSpanFull()
->persistTabInQueryString(),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
TextColumn::make('id')
->label(__('filament::resources.columns.id')),
HabboBadgeColumn::make('badge')
->alignCenter()
->label(__('filament::resources.columns.image')),
TextColumn::make('rank_name')
->label(__('filament::resources.columns.name'))
->description(fn (Model $record) => Str::limit($record->description, 40))
->tooltip(function (Model $record): ?string {
$description = $record->description;
if (strlen($description) <= 40) {
return null;
}
return $description;
})
->searchable(),
TextColumn::make('prefix')
->label(__('filament::resources.columns.prefix'))
->description(fn (Model $record) => $record->prefix_color)
->searchable(),
ToggleColumn::make('hidden_rank')
->label(__('filament::resources.columns.is_hidden')),
])
->filters([
//
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([
]);
}
#[\Override]
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListPermissions::route('/'),
'create' => CreatePermission::route('/create'),
'view' => ViewPermission::route('/{record}'),
'edit' => EditPermission::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Tags\Pages;
use App\Filament\Resources\Atom\Tags\TagResource;
use Filament\Resources\Pages\CreateRecord;
class CreateTag extends CreateRecord
{
protected static string $resource = TagResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Tags\Pages;
use App\Filament\Resources\Atom\Tags\TagResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditTag extends EditRecord
{
protected static string $resource = TagResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Tags\Pages;
use App\Filament\Resources\Atom\Tags\TagResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListTags extends ListRecords
{
protected static string $resource = TagResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Tags\Pages;
use App\Filament\Resources\Atom\Tags\TagResource;
use Filament\Resources\Pages\ViewRecord;
class ViewTag extends ViewRecord
{
protected static string $resource = TagResource::class;
}
@@ -1,52 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Tags\RelationManagers;
use App\Filament\Resources\Atom\Articles\ArticleResource;
use App\Filament\Traits\TranslatableResource;
use Filament\Actions\AttachAction;
use Filament\Actions\DetachAction;
use Filament\Actions\DetachBulkAction;
use Filament\Actions\ViewAction;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
class ArticlesRelationManager extends RelationManager
{
use TranslatableResource;
// Use camelCase to match the method in the Tag model
protected static string $relationship = 'websiteArticles';
protected static ?string $recordTitleAttribute = 'title';
public static string $translateIdentifier = 'article';
public function form(Schema $schema): Schema
{
return $schema
->components(ArticleResource::getForm());
}
public function table(Table $table): Table
{
return $table
->columns(ArticleResource::getTable())
->modifyQueryUsing(fn ($query) => $query->latest())
->filters([
//
])
->headerActions([
AttachAction::make()
->preloadRecordSelect(),
])
->recordActions([
ViewAction::make(),
DetachAction::make(),
])
->toolbarActions([
DetachBulkAction::make(),
]);
}
}
@@ -1,125 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Tags;
use App\Filament\Resources\Atom\Tags\Pages\CreateTag;
use App\Filament\Resources\Atom\Tags\Pages\EditTag;
use App\Filament\Resources\Atom\Tags\Pages\ListTags;
use App\Filament\Resources\Atom\Tags\Pages\ViewTag;
use App\Filament\Resources\Atom\Tags\RelationManagers\ArticlesRelationManager;
use App\Filament\Traits\TranslatableResource;
use App\Models\Articles\Tag;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\ColorPicker;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\ColorColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class TagResource extends Resource
{
use TranslatableResource;
protected static ?string $model = Tag::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-tag';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'website/tags';
public static string $translateIdentifier = 'tags';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components(static::getForm());
}
public static function getForm(): array
{
return [
Tabs::make('Main')
->tabs([
Tab::make(__('filament::resources.tabs.Home'))
->icon('heroicon-o-home')
->schema([
TextInput::make('name')
->label(__('filament::resources.inputs.name'))
->required()
->maxLength(255)
->autocomplete()
->columnSpan('full'),
ColorPicker::make('background_color')
->label(__('filament::resources.inputs.background_color'))
->required()
->columnSpan('full'),
]),
])->columnSpanFull(),
];
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns(static::getTable())
->filters([
//
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([
DeleteBulkAction::make(),
]);
}
public static function getTable(): array
{
return [
TextColumn::make('id')
->label(__('filament::resources.columns.id')),
TextColumn::make('name')
->label(__('filament::resources.columns.name'))
->searchable()
->limit(50),
ColorColumn::make('background_color')
->label(__('filament::resources.columns.background_color'))
->searchable()
->copyable()
->copyMessage(__('filament::resources.common.Sucessfull'))
->copyMessageDuration(1500),
];
}
#[\Override]
public static function getRelations(): array
{
return [
ArticlesRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => ListTags::route('/'),
'create' => CreateTag::route('/create'),
'view' => ViewTag::route('/{record}'),
'edit' => EditTag::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Teams\Pages;
use App\Filament\Resources\Atom\Teams\TeamResource;
use Filament\Resources\Pages\CreateRecord;
class CreateTeam extends CreateRecord
{
protected static string $resource = TeamResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Teams\Pages;
use App\Filament\Resources\Atom\Teams\TeamResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditTeam extends EditRecord
{
protected static string $resource = TeamResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Teams\Pages;
use App\Filament\Resources\Atom\Teams\TeamResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListTeams extends ListRecords
{
protected static string $resource = TeamResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,118 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\Teams;
use App\Filament\Resources\Atom\Teams\Pages\CreateTeam;
use App\Filament\Resources\Atom\Teams\Pages\EditTeam;
use App\Filament\Resources\Atom\Teams\Pages\ListTeams;
use App\Filament\Tables\Columns\HabboBadgeColumn;
use App\Filament\Traits\TranslatableResource;
use App\Models\Community\Staff\WebsiteTeam;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
class TeamResource extends Resource
{
use TranslatableResource;
protected static ?string $model = WebsiteTeam::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-user-group';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'website/teams';
public static string $translateIdentifier = 'teams';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
TextInput::make('rank_name')
->autofocus()
->maxLength(255)
->required()
->label(__('filament::resources.inputs.name')),
TextInput::make('job_description')
->maxLength(255)
->label(__('filament::resources.inputs.description')),
TextInput::make('badge')
->maxLength(255)
->label(__('filament::resources.inputs.badge_code'))
->required(),
Toggle::make('hidden_rank')
->label(__('filament::resources.inputs.is_hidden')),
]),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
TextColumn::make('id')
->label(__('filament::resources.columns.id')),
HabboBadgeColumn::make('badge')
->label(__('filament::resources.columns.badge')),
TextColumn::make('rank_name')
->label(__('filament::resources.columns.name')),
TextColumn::make('job_description')
->label(__('filament::resources.inputs.description')),
IconColumn::make('hidden_rank')
->label(__('filament::resources.columns.is_hidden'))
->icon(fn (Model $record) => $record->hidden_rank ? 'heroicon-o-check-circle' : 'heroicon-o-x-circle')
->colors([
'danger' => false,
'success' => true,
]),
])
->filters([
//
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
DeleteBulkAction::make(),
]);
}
#[\Override]
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListTeams::route('/'),
'create' => CreateTeam::route('/create'),
'edit' => EditTeam::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\WebsiteDrawBadges\Pages;
use App\Filament\Resources\Atom\WebsiteDrawBadges\WebsiteDrawBadgeResource;
use Filament\Resources\Pages\EditRecord;
class EditWebsiteDrawBadge extends EditRecord
{
protected static string $resource = WebsiteDrawBadgeResource::class;
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\WebsiteDrawBadges\Pages;
use App\Filament\Resources\Atom\WebsiteDrawBadges\WebsiteDrawBadgeResource;
use Filament\Resources\Pages\ListRecords;
class ListWebsiteDrawBadge extends ListRecords
{
protected static string $resource = WebsiteDrawBadgeResource::class;
}
@@ -1,176 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\WebsiteDrawBadges;
use App\Filament\Resources\Atom\WebsiteDrawBadges\Pages\EditWebsiteDrawBadge;
use App\Filament\Resources\Atom\WebsiteDrawBadges\Pages\ListWebsiteDrawBadge;
use App\Models\WebsiteDrawBadge;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Table;
use Illuminate\Support\Facades\DB;
class WebsiteDrawBadgeResource extends Resource
{
protected static ?string $model = WebsiteDrawBadge::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-trophy';
protected static string|\UnitEnum|null $navigationGroup = 'Website';
protected static ?string $slug = 'draw-badges';
protected static ?string $pluralModelLabel = 'draw badges';
protected static ?string $navigationLabel = 'Draw Badges';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('badge_name')
->label(__('Badge Name'))
->nullable()
->maxLength(24)
->autocomplete(false),
TextInput::make('badge_desc')
->label(__('Badge Description'))
->nullable()
->maxLength(255)
->autocomplete(false)
->columnSpanFull(),
Toggle::make('published')
->label(__('Published'))
->default(false),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
TextColumn::make('id')
->label(__('ID'))
->sortable(),
TextColumn::make('user_id')
->label(__('User ID')),
TextColumn::make('user.username')
->label(__('Username'))
->sortable()
->searchable(),
TextColumn::make('badge_name')
->limit(8)
->label(__('Badge Name')),
TextColumn::make('badge_desc')
->label(__('Badge description'))
->limit(35)
->tooltip(function (TextColumn $column): ?string {
$state = $column->getState();
if (strlen($state) <= $column->getCharacterLimit()) {
return null;
}
return $state;
}),
TextColumn::make('created_at')
->label(__('Created At'))
->dateTime(),
ImageColumn::make('badge_url')
->label(__('Badge'))
->getStateUsing(fn ($record) => config('app.url') . $record->badge_url)
->extraAttributes(['style' => 'image-rendering: pixelated'])
->size(40),
ToggleColumn::make('published')
->label(__('Published')),
])
->recordActions([
DeleteAction::make()
->before(function (DeleteAction $action, WebsiteDrawBadge $record): void {
$badgeCode = pathinfo($record->badge_path, PATHINFO_FILENAME);
// Remove the badge from any user before deleting it.
if ($record->published) {
DB::table('users_badges')
->where('user_id', $record->user_id)
->where('badge_code', $badgeCode)
->delete();
}
// Remove from JSON
$filePath = DB::table('website_settings')->where('key', 'nitro_external_texts_file')->value('value');
if ($filePath && file_exists($filePath) && is_writable($filePath)) {
$json = json_decode(file_get_contents($filePath), true);
unset($json["badge_name_{$badgeCode}"]);
unset($json["badge_desc_{$badgeCode}"]);
file_put_contents($filePath, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
// Delete the badge file from the filesystem
$badgePath = $record->badge_path;
if ($badgePath && file_exists($badgePath)) {
unlink($badgePath);
}
}),
])
->toolbarActions([
DeleteBulkAction::make()
->before(function (DeleteBulkAction $action, $records): void {
foreach ($records as $record) {
$badgeCode = pathinfo((string) $record->badge_path, PATHINFO_FILENAME);
// Remove the badge from any user before deleting it.
if ($record->published) {
DB::table('users_badges')
->where('user_id', $record->user_id)
->where('badge_code', $badgeCode)
->delete();
}
$filePath = DB::table('website_settings')->where('key', 'nitro_external_texts_file')->value('value');
if ($filePath && file_exists($filePath) && is_writable($filePath)) {
$json = json_decode(file_get_contents($filePath), true);
unset($json["badge_name_{$badgeCode}"]);
unset($json["badge_desc_{$badgeCode}"]);
file_put_contents($filePath, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
$badgePath = $record->badge_path;
if ($badgePath && file_exists($badgePath)) {
unlink($badgePath);
}
}
}),
]);
}
#[\Override]
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => ListWebsiteDrawBadge::route('/'),
'edit' => EditWebsiteDrawBadge::route('/{record}/edit'),
];
}
public static function canCreate(): bool
{
return false;
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Atom\WriteableBoxResource\Pages;
use App\Filament\Resources\Atom\WriteableBoxResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ManageRecords;
class ManageWriteableBoxes extends ManageRecords
{
protected static string $resource = WriteableBoxResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,25 +0,0 @@
<?php
namespace App\Filament\Resources\DashboardResource\Widgets;
use App\Filament\Resources\Shop\ShopOrderResource;
use App\Models\User\UserOrder;
use Filament\Actions\ViewAction;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget as BaseWidget;
class LatestOrders extends BaseWidget
{
protected int|string|array $columnSpan = 'full';
public function table(Table $table): Table
{
return $table
->query(UserOrder::latest())
->paginated([3, 5, 8])
->columns(ShopOrderResource::getTable())
->recordActions([
ViewAction::make()->schema(ShopOrderResource::getForm()),
]);
}
}
@@ -1,79 +0,0 @@
<?php
namespace App\Filament\Resources\DashboardResource\Widgets;
use App\Models\User\UserOrder;
use Filament\Widgets\ChartWidget;
use Flowframe\Trend\Trend;
use Flowframe\Trend\TrendValue;
use Illuminate\Contracts\Support\Htmlable;
class OrdersAggregateChart extends ChartWidget
{
protected ?string $maxHeight = '300px';
protected string $color = 'secondary';
#[\Override]
public function getHeading(): string|Htmlable|null
{
return __('filament::resources.stats.orders_chart.title');
}
#[\Override]
public function getDescription(): string|Htmlable|null
{
return __('filament::resources.stats.orders_chart.description');
}
#[\Override]
protected function getData(): array
{
$pendingOrder = Trend::query(UserOrder::pending())
->between(start: now()->startOfMonth(), end: now()->endOfMonth())
->perDay()
->count();
$cancelledOrder = Trend::query(UserOrder::cancelled())
->between(start: now()->startOfMonth(), end: now()->endOfMonth())
->perDay()
->count();
$completedOrder = Trend::query(UserOrder::completed())
->between(start: now()->startOfMonth(), end: now()->endOfMonth())
->perDay()
->count();
$datasets = [
$this->getDataset($pendingOrder, __('filament::resources.stats.orders_chart.pending'), '#fbbf24', '#f59e0b'),
$this->getDataset($cancelledOrder, __('filament::resources.stats.orders_chart.cancelled'), '#dc2626', '#b91c1c'),
$this->getDataset($completedOrder, __('filament::resources.stats.orders_chart.completed'), '#10b981', '#059669'),
];
$data = $pendingOrder->map(fn (TrendValue $value) => $value->date)->merge(
$cancelledOrder->map(fn (TrendValue $value) => $value->date),
)->merge(
$completedOrder->map(fn (TrendValue $value) => $value->date),
)->unique()->sort()->flatten();
return [
'datasets' => $datasets,
'labels' => $data,
];
}
protected function getDataset($data, $label, string $backgroundColor, string $borderColor): array
{
return [
'label' => $label,
'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
'backgroundColor' => $backgroundColor,
'borderColor' => $borderColor,
];
}
protected function getType(): string
{
return 'bar';
}
}
@@ -1,176 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\Achievements;
use App\Enums\AchievementCategory;
use App\Enums\CurrencyTypes;
use App\Filament\Resources\Hotel\Achievements\Pages\CreateAchievement;
use App\Filament\Resources\Hotel\Achievements\Pages\EditAchievement;
use App\Filament\Resources\Hotel\Achievements\Pages\ListAchievements;
use App\Filament\Resources\Hotel\Achievements\Pages\ViewAchievement;
use App\Filament\Tables\Columns\HabboBadgeColumn;
use App\Filament\Traits\TranslatableResource;
use App\Models\Achievement;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
class AchievementResource extends Resource
{
use TranslatableResource;
protected static ?string $model = Achievement::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-academic-cap';
protected static string|\UnitEnum|null $navigationGroup = 'Hotel';
public static string $translateIdentifier = 'achievements';
protected static ?string $slug = 'hotel/achievements';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
Tabs::make('Main')
->tabs([
Tab::make(__('filament::resources.tabs.Home'))
->icon('heroicon-o-home')
->schema([
TextInput::make('name')
->label(__('filament::resources.inputs.name'))
->required()
->maxLength(64)
->autocomplete()
->columnSpan('full'),
TextInput::make('level')
->label(__('filament::resources.inputs.level'))
->numeric()
->required()
->autocomplete()
->columnSpan('full'),
Select::make('category')
->native(false)
->label(__('filament::resources.inputs.category'))
->options(AchievementCategory::toInput()),
]),
Tab::make(__('filament::resources.tabs.Configurations'))
->icon('heroicon-o-cog')
->schema([
Select::make('visible')
->native(false)
->label(__('filament::resources.inputs.visible'))
->options([
'1' => __('filament::resources.common.Yes'),
'0' => __('filament::resources.common.No'),
]),
Select::make('reward_type')
->native(false)
->label(__('filament::resources.inputs.reward_type'))
->options(CurrencyTypes::toInput()),
TextInput::make('reward_amount')
->label(__('filament::resources.inputs.reward_amount'))
->numeric()
->required(),
TextInput::make('points')
->label(__('filament::resources.inputs.points'))
->helperText(__('filament::resources.helpers.achievement_points'))
->numeric()
->required(),
TextInput::make('progress_needed')
->label(__('filament::resources.inputs.progress_needed'))
->helperText(__('filament::resources.helpers.achievement_progress_needed'))
->numeric()
->required(),
]),
])->columnSpanFull(),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
TextColumn::make('id')
->label(__('filament::resources.columns.id')),
HabboBadgeColumn::make('badge')
->label(__('filament::resources.columns.badge')),
TextColumn::make('name')
->label(__('filament::resources.columns.name'))
->searchable(),
TextColumn::make('level')
->label(__('filament::resources.columns.level')),
TextColumn::make('category')
->badge()
->searchable()
->label(__('filament::resources.columns.category'))
->toggleable(),
ToggleColumn::make('visible')
->label(__('filament::resources.columns.visible'))
->disabled()
->toggleable(),
])
->filters([
SelectFilter::make('visible')
->options([
'1' => __('filament::resources.common.Yes'),
'0' => __('filament::resources.common.No'),
])
->label(__('filament::resources.columns.visible'))
->placeholder(__('filament::resources.common.All')),
SelectFilter::make('category')
->options(AchievementCategory::toInput())
->label(__('filament::resources.columns.category'))
->placeholder(__('filament::resources.common.All')),
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([]);
}
#[\Override]
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListAchievements::route('/'),
'create' => CreateAchievement::route('/create'),
'view' => ViewAchievement::route('/{record}'),
'edit' => EditAchievement::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\Achievements\Pages;
use App\Filament\Resources\Hotel\Achievements\AchievementResource;
use Filament\Resources\Pages\CreateRecord;
class CreateAchievement extends CreateRecord
{
protected static string $resource = AchievementResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\Achievements\Pages;
use App\Filament\Resources\Hotel\Achievements\AchievementResource;
use Filament\Pages\Actions;
use Filament\Resources\Pages\EditRecord;
class EditAchievement extends EditRecord
{
protected static string $resource = AchievementResource::class;
protected function getActions(): array
{
return [
// Actions\DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\Achievements\Pages;
use App\Filament\Resources\Hotel\Achievements\AchievementResource;
use Filament\Pages\Actions;
use Filament\Resources\Pages\ListRecords;
class ListAchievements extends ListRecords
{
protected static string $resource = AchievementResource::class;
protected function getActions(): array
{
return [
// Actions\CreateAction::make(),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\Achievements\Pages;
use App\Filament\Resources\Hotel\Achievements\AchievementResource;
use Filament\Resources\Pages\ViewRecord;
class ViewAchievement extends ViewRecord
{
protected static string $resource = AchievementResource::class;
}
@@ -1,101 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\BadgeTextEditors;
use App\Filament\Resources\Hotel\BadgeTextEditors\Pages\CreateBadgeTextEditor;
use App\Filament\Resources\Hotel\BadgeTextEditors\Pages\EditBadgeTextEditor;
use App\Filament\Resources\Hotel\BadgeTextEditors\Pages\ListBadgeTextEditors;
use App\Models\WebsiteBadge;
use App\Services\SettingsService;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Support\Str;
class BadgeTextEditorResource extends Resource
{
protected static ?string $model = WebsiteBadge::class;
protected static string|\UnitEnum|null $navigationGroup = 'Hotel';
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-pencil-square';
protected static ?string $navigationLabel = 'Badge Editor';
protected static ?string $modelLabel = 'Badge Text';
protected static ?string $slug = 'hotel/badge-text-editor';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('badge_key')
->required()
->label('Badge Key - Expl. ATOM101')
->placeholder('This is the badge code'),
TextInput::make('badge_name')
->required()
->label('Badge Name')
->placeholder('This is the name of the badge: Expl. The ATOM Badge'),
Textarea::make('badge_description')
->required()
->label('Badge Description')
->placeholder('Please add a description for the badge.'),
]);
}
#[\Override]
public static function table(Table $table): Table
{
$settingsService = app(SettingsService::class);
$badgesPath = $settingsService->getOrDefault('badges_path', '/gamedata/c_images/album1584/');
return $table
->columns([
ImageColumn::make('badge_key')
->label('Badge Image')
->getStateUsing(function ($record) use ($badgesPath) {
$badgeName = str_replace('badge_desc_', '', $record->badge_key);
return asset($badgesPath . $badgeName . '.gif');
})
->width(50)
->height(50),
TextColumn::make('badge_name')
->label('Badge Code & Name')
->formatStateUsing(fn ($record) => $record->badge_key . ' : ' . $record->badge_name)
->searchable(query: function ($query, $search): void {
$query->where('badge_key', 'like', "%{$search}%")
->orWhere('badge_name', 'like', "%{$search}%");
})
->sortable(),
TextColumn::make('badge_description')
->label('Badge Description')
->getStateUsing(fn ($record) => Str::limit($record->badge_description, 65))
->searchable(),
])
->filters([])
->defaultSort('badge_key', 'asc')
->recordActions([
EditAction::make(),
DeleteAction::make(),
]);
}
public static function getPages(): array
{
return [
'index' => ListBadgeTextEditors::route('/'),
'create' => CreateBadgeTextEditor::route('/create'),
'edit' => EditBadgeTextEditor::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\BadgeTextEditors\Pages;
use App\Filament\Resources\Hotel\BadgeTextEditors\BadgeTextEditorResource;
use Filament\Resources\Pages\CreateRecord;
class CreateBadgeTextEditor extends CreateRecord
{
protected static string $resource = BadgeTextEditorResource::class;
}
@@ -1,51 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\BadgeTextEditors\Pages;
use App\Filament\Resources\Hotel\BadgeTextEditors\BadgeTextEditorResource;
use Filament\Actions\DeleteAction;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use PDOException;
class EditBadgeTextEditor extends EditRecord
{
protected static string $resource = BadgeTextEditorResource::class;
protected function getActions(): array
{
return [DeleteAction::make()];
}
#[\Override]
protected function mutateFormDataBeforeSave(array $data): array
{
return $data;
}
protected function afterSave(): void {}
#[\Override]
protected function handleRecordUpdate(Model $record, array $data): Model
{
try {
return parent::handleRecordUpdate($record, $data);
} catch (PDOException $e) {
if ($e->getCode() === '23000') {
Log::error('Duplicate badge key error: ' . $e->getMessage());
Notification::make()
->title('Duplicate Badge Key')
->body('The badge key already exists. Please use a unique badge key.')
->danger()
->persistent()
->send();
return $record;
}
throw $e;
}
}
}
@@ -1,153 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\BadgeTextEditors\Pages;
use App\Filament\Resources\Hotel\BadgeTextEditors\BadgeTextEditorResource;
use App\Models\WebsiteBadge;
use App\Services\SettingsService;
use Exception;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;
use Illuminate\Support\Facades\Log;
class ListBadgeTextEditors extends ListRecords
{
protected static string $resource = BadgeTextEditorResource::class;
protected function getActions(): array
{
return [
CreateAction::make()
->label('Add Badge')
->color('info')
->modalHeading('Add a New Badge')
->modalButton('Create Badge')
->after(function (): void {
Notification::make()
->title('Badge Created')
->body('The badge was successfully created.')
->success()
->send();
}),
Action::make('export')
->label('Export to ExternalTexts')
->action('exportToJson'),
Action::make('backup')
->label('Create Backup of ExternalTexts')
->color('success')
->action('createBackup'),
];
}
public function exportToJson(SettingsService $settingsService)
{
$jsonPath = $settingsService->getOrDefault('nitro_external_texts_file');
if ($jsonPath === '' || $jsonPath === '0') {
Notification::make()
->title('Export Failed')
->body('The JSON file path is not configured in the website settings.')
->danger()
->send();
return;
}
if (! file_exists($jsonPath)) {
Notification::make()
->title('Export Failed')
->body('The JSON file does not exist at the specified path.')
->danger()
->send();
return;
}
$jsonData = json_decode(file_get_contents($jsonPath), true);
$badges = WebsiteBadge::all();
$badgeKeys = $badges->pluck('badge_key')->toArray();
foreach ($jsonData as $key => $value) {
if (
(str_starts_with((string) $key, 'badge_desc_') || str_starts_with((string) $key, 'badge_name_')) &&
! in_array(str_replace(['badge_desc_', 'badge_name_'], '', $key), $badgeKeys)
) {
unset($jsonData[$key]);
}
}
foreach ($badges as $badge) {
$jsonData['badge_desc_' . $badge->badge_key] = $badge->badge_description;
$jsonData['badge_name_' . $badge->badge_key] = $badge->badge_name;
}
try {
$result = file_put_contents(
$jsonPath,
json_encode($jsonData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
);
if ($result === false) {
throw new Exception('Failed to write to the JSON file.');
}
Notification::make()
->title('Export Successful')
->body('Badge data exported successfully.')
->success()
->send();
} catch (Exception $e) {
Log::error('Failed to export badge data: ' . $e->getMessage());
Notification::make()
->title('Export Failed')
->body('Failed to export badge data. Please check file permissions or contact your administrator.')
->danger()
->send();
}
}
public function createBackup(SettingsService $settingsService)
{
$jsonPath = $settingsService->getOrDefault('nitro_external_texts_file');
if ($jsonPath === '' || $jsonPath === '0') {
Notification::make()
->title('Backup Failed')
->body('The JSON file path is not configured in the website settings.')
->danger()
->send();
return;
}
if (! file_exists($jsonPath)) {
Notification::make()
->title('Backup Failed')
->body('The JSON file does not exist at the specified path.')
->danger()
->send();
return;
}
$backupPath = dirname($jsonPath) . '/ExternalTexts_' . time() . '.json';
if (copy($jsonPath, $backupPath)) {
Notification::make()
->title('Backup Successful')
->body('A backup of the JSON file has been created: ' . basename($backupPath))
->success()
->send();
} else {
Notification::make()
->title('Backup Failed')
->body('Failed to create a backup of the JSON file.')
->danger()
->send();
}
}
}
@@ -1,69 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\BadgeUploads;
use App\Filament\Resources\Hotel\BadgeUploads\Pages\ManageBadgeUploads;
use Filament\Forms\Components\FileUpload;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Support\Facades\Storage;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
class BadgeUploadResource extends Resource
{
protected static string|\UnitEnum|null $navigationGroup = 'Hotel';
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-gif';
protected static ?string $label = 'Badge Upload';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
FileUpload::make('badge_file')
->label('Upload Badge')
->disk('local')
->directory(setting('badge_path_filesystem'))
->required()
->getUploadedFileNameForStorageUsing(
fn (TemporaryUploadedFile $file): string => strtolower(str_replace([' ', '-', 'æ', 'ø', 'å'], ['_', '_', 'ae', 'oe', 'aa'], $file->getClientOriginalName())),
),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('filename')
->label('File Name')
->sortable(),
TextColumn::make('path')
->label('File Path'),
])
->filters([]);
}
public static function getPages(): array
{
return [
'index' => ManageBadgeUploads::route('/'),
];
}
public static function getFiles(): array
{
$badgePath = env('BadgePath', 'badges');
$files = Storage::disk('local')->files($badgePath);
return collect($files)->map(fn ($file) => [
'filename' => basename($file),
'path' => $file,
])->toArray();
}
}
@@ -1,48 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\BadgeUploads\Pages;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\Page; // Import the Notification class
class ManageBadgeUploads extends Page implements HasForms
{
use InteractsWithForms;
public $badge_file;
protected static string $resource = \App\Filament\Resources\Hotel\BadgeUploads\BadgeUploadResource::class;
protected string $view = 'filament.pages.manage-badge-uploads';
public function mount(): void
{
$this->form->fill([]);
}
protected function getFormSchema(): array
{
return [
FileUpload::make('badge_file')
->label('Upload Badge')
->disk('badges')
->preserveFilenames()
->acceptedFileTypes(['image/gif'])
->rules(['mimes:gif'])
->required(),
];
}
public function save(): void
{
$this->form->getState();
Notification::make()
->title('Badge uploaded successfully!')
->success()
->send();
}
}
@@ -1,180 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CatalogPages;
use App\Filament\Resources\Hotel\CatalogPages\Pages\CreateCatalogPage;
use App\Filament\Resources\Hotel\CatalogPages\Pages\EditCatalogPage;
use App\Filament\Resources\Hotel\CatalogPages\Pages\ListCatalogPages;
use App\Filament\Resources\Hotel\CatalogPages\RelationManagers\CatalogItemsRelationManager;
use App\Models\Game\Furniture\CatalogPage;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class CatalogPageResource extends Resource
{
protected static ?string $model = CatalogPage::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-rectangle-stack';
protected static string|\UnitEnum|null $navigationGroup = 'Hotel';
public static string $translateIdentifier = 'catalog-pages';
protected static ?string $slug = 'hotel/catalog-pages';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('parent_id')
->required()
->integer(),
TextInput::make('caption_save')
->required(),
TextInput::make('caption')
->required(),
TextInput::make('page_layout')
->required(),
TextInput::make('icon_color')
->required()
->integer(),
TextInput::make('icon_image')
->required()
->integer(),
TextInput::make('min_rank')
->required()
->integer(),
TextInput::make('order_num')
->required()
->integer(),
TextInput::make('visible')
->required(),
TextInput::make('enabled')
->required(),
TextInput::make('club_only')
->required(),
TextInput::make('vip_only')
->required(),
TextInput::make('page_headline')
->required(),
TextInput::make('page_teaser')
->required(),
TextInput::make('page_special'),
TextInput::make('page_text1'),
TextInput::make('page_text2'),
TextInput::make('page_text_details'),
TextInput::make('page_text_teaser'),
TextInput::make('room_id')
->integer(),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('parent_id'),
TextColumn::make('caption_save'),
TextColumn::make('caption')
->searchable()
->sortable(),
TextColumn::make('page_layout'),
TextColumn::make('icon_color'),
ImageColumn::make('icon_image'),
TextColumn::make('min_rank'),
TextColumn::make('order_num'),
TextColumn::make('visible'),
TextColumn::make('enabled'),
TextColumn::make('club_only'),
TextColumn::make('vip_only'),
TextColumn::make('page_headline'),
TextColumn::make('page_teaser'),
TextColumn::make('page_special'),
TextColumn::make('page_text1'),
TextColumn::make('page_text2'),
TextColumn::make('room_id'),
TextColumn::make('includes'),
])
->filters([
//
])
->recordActions([
EditAction::make(),
DeleteAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
public static function getPages(): array
{
return [
'index' => ListCatalogPages::route('/'),
'create' => CreateCatalogPage::route('/create'),
'edit' => EditCatalogPage::route('/{record}/edit'),
];
}
#[\Override]
public static function getRelations(): array
{
return [
CatalogItemsRelationManager::class,
];
}
public static function getGloballySearchableAttributes(): array
{
return ['caption'];
}
}
@@ -1,18 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CatalogPages\Pages;
use App\Filament\Resources\Hotel\CatalogPages\CatalogPageResource;
use Filament\Resources\Pages\CreateRecord;
class CreateCatalogPage extends CreateRecord
{
protected static string $resource = CatalogPageResource::class;
protected function getHeaderActions(): array
{
return [
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CatalogPages\Pages;
use App\Filament\Resources\Hotel\CatalogPages\CatalogPageResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditCatalogPage extends EditRecord
{
protected static string $resource = CatalogPageResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CatalogPages\Pages;
use App\Filament\Resources\Hotel\CatalogPages\CatalogPageResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListCatalogPages extends ListRecords
{
protected static string $resource = CatalogPageResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}
@@ -1,467 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CatalogPages\RelationManagers;
use App\Models\Game\Furniture\ItemBase;
use Filament\Actions\Action;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\CreateAction;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class CatalogItemsRelationManager extends RelationManager
{
protected static string $relationship = 'catalogItems';
public function form(Schema $schema): Schema
{
return $schema
->components([
Select::make('item_ids')
->label('Furniture Item')
->relationship(
name: 'itemBase',
titleAttribute: 'item_name',
modifyQueryUsing: fn (Builder $query) => $query->orderBy('item_name'),
)
->searchable()
->required()
->preload()
->createOptionForm([
TextInput::make('sprite_id')
->label('Sprite ID')
->numeric()
->default(0),
TextInput::make('public_name')
->maxLength(56),
TextInput::make('item_name')
->required()
->maxLength(70),
TextInput::make('type')
->default('s')
->maxLength(3),
Grid::make(3)
->schema([
TextInput::make('width')
->numeric()
->default(1),
TextInput::make('length')
->numeric()
->default(1),
TextInput::make('stack_height')
->numeric()
->default(0.00),
]),
Grid::make(3)
->schema([
Toggle::make('allow_stack')
->default(true),
Toggle::make('allow_sit')
->default(false),
Toggle::make('allow_lay')
->default(false),
]),
Grid::make(3)
->schema([
Toggle::make('allow_walk')
->default(false),
Toggle::make('allow_gift')
->default(true),
Toggle::make('allow_trade')
->default(true),
]),
Grid::make(3)
->schema([
Toggle::make('allow_recycle')
->default(false),
Toggle::make('allow_marketplace_sell')
->default(false),
Toggle::make('allow_inventory_stack')
->default(true),
]),
TextInput::make('interaction_type')
->default('default')
->maxLength(500),
Grid::make(2)
->schema([
TextInput::make('interaction_modes_count')
->numeric()
->default(1),
TextInput::make('vending_ids')
->default('0')
->maxLength(255),
]),
Grid::make(2)
->schema([
TextInput::make('multiheight')
->default('0')
->maxLength(50),
TextInput::make('customparams')
->maxLength(256),
]),
Grid::make(2)
->schema([
TextInput::make('effect_id_male')
->numeric()
->default(0),
TextInput::make('effect_id_female')
->numeric()
->default(0),
]),
TextInput::make('clothing_on_walk')
->maxLength(255),
])
->columnSpanFull(),
TextInput::make('catalog_name')
->label('Catalog Name')
->required()
->maxLength(100)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
Grid::make(2)
->schema([
TextInput::make('cost_credits')
->label('Cost Credits')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? '')
->default(3),
TextInput::make('cost_points')
->label('Cost Points')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? '')
->default(0),
]),
Grid::make(2)
->schema([
TextInput::make('points_type')
->label('Points Type')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? '')
->default(0),
TextInput::make('amount')
->label('Amount')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? '')
->default(1),
]),
Grid::make(2)
->schema([
Toggle::make('limited_stack')
->label('Limited Stack')
->dehydrateStateUsing(fn ($state) => $state ? '1' : '0'),
Toggle::make('limited_sells')
->label('Limited Sells')
->dehydrateStateUsing(fn ($state) => $state ? '1' : '0'),
]),
Grid::make(3)
->schema([
TextInput::make('order_number')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? '')
->default(1),
TextInput::make('offer_id')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('song_id')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? '')
->default(0),
]),
Textarea::make('extradata')
->label('Extra Data')
->maxLength(500)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
Grid::make(2)
->schema([
Toggle::make('have_offer')
->label('Have Offer')
->default(true)
->dehydrateStateUsing(fn ($state) => $state ? '1' : '0'),
Toggle::make('club_only')
->label('Club Only')
->default(false)
->dehydrateStateUsing(fn ($state) => $state ? '1' : '0'),
]),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('catalog_name')
->columns([
ImageColumn::make('icon')
->getStateUsing(fn ($record) => url($record->itemBase?->icon()))
->size('25px')
->label('Icon')
->circular(),
TextColumn::make('itemBase.item_name')
->label('Furniture Name')
->sortable()
->searchable(),
TextColumn::make('catalog_name')
->label('Catalog Name')
->sortable()
->searchable(),
TextColumn::make('cost_credits')
->label('Credits')
->sortable(),
TextColumn::make('cost_points')
->label('Points')
->sortable(),
IconColumn::make('limited_stack')
->label('Limited')
->boolean(),
IconColumn::make('club_only')
->label('HC Only')
->boolean(),
TextColumn::make('itemBase.type')
->label('Type')
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('itemBase.width')
->label('Width')
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('itemBase.length')
->label('Length')
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('order_number')
->label('Order')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('type')
->query(fn (Builder $query, array $data): Builder => empty($data['values'])
? $query
: $query->whereHas('itemBase', function (Builder $query) use ($data): void {
$query->whereIn('type', $data['values']);
}))
->options(
fn () => ItemBase::query()
->select('type')
->distinct()
->orderBy('type')
->pluck('type', 'type')
->toArray(),
)
->multiple()
->searchable()
->preload(),
TernaryFilter::make('club_only')
->label('HC Only'),
TernaryFilter::make('limited_stack')
->label('Limited'),
])
->defaultSort('order_number')
->headerActions([
CreateAction::make(),
])
->recordActions([
EditAction::make()->label('Edit Catalog Item'),
Action::make('editItemBase')
->label('Edit Item base')
->icon('heroicon-m-cube')
->modalWidth('3xl')
->modalHeading('Edit Item Base')
->fillForm(function ($record) {
$itemBase = $record->itemBase;
if (! $itemBase) {
return [];
}
return [
'sprite_id' => $itemBase->sprite_id,
'public_name' => $itemBase->public_name,
'item_name' => $itemBase->item_name,
'type' => $itemBase->type,
'width' => $itemBase->width,
'length' => $itemBase->length,
'stack_height' => $itemBase->stack_height,
'allow_stack' => $itemBase->allow_stack,
'allow_sit' => $itemBase->allow_sit,
'allow_lay' => $itemBase->allow_lay,
'allow_walk' => $itemBase->allow_walk,
'allow_gift' => $itemBase->allow_gift,
'allow_trade' => $itemBase->allow_trade,
'allow_recycle' => $itemBase->allow_recycle,
'allow_marketplace_sell' => $itemBase->allow_marketplace_sell,
'allow_inventory_stack' => $itemBase->allow_inventory_stack,
'interaction_type' => $itemBase->interaction_type,
'interaction_modes_count' => $itemBase->interaction_modes_count,
'vending_ids' => $itemBase->vending_ids,
'multiheight' => $itemBase->multiheight,
'customparams' => $itemBase->customparams,
'effect_id_male' => $itemBase->effect_id_male,
'effect_id_female' => $itemBase->effect_id_female,
'clothing_on_walk' => $itemBase->clothing_on_walk,
];
})
->schema([
TextInput::make('sprite_id')
->label('Sprite ID')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('public_name')
->label('Public Name')
->maxLength(56)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('item_name')
->label('Item Name')
->required()
->maxLength(70),
TextInput::make('type')
->maxLength(3)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
Grid::make(3)
->schema([
TextInput::make('width')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('length')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('stack_height')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
]),
Grid::make(3)
->schema([
Toggle::make('allow_stack'),
Toggle::make('allow_sit'),
Toggle::make('allow_lay'),
]),
Grid::make(3)
->schema([
Toggle::make('allow_walk'),
Toggle::make('allow_gift'),
Toggle::make('allow_trade'),
]),
Grid::make(3)
->schema([
Toggle::make('allow_recycle'),
Toggle::make('allow_marketplace_sell'),
Toggle::make('allow_inventory_stack'),
]),
TextInput::make('interaction_type')
->maxLength(500)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
Grid::make(2)
->schema([
TextInput::make('interaction_modes_count')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('vending_ids')
->maxLength(255)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
]),
Grid::make(2)
->schema([
TextInput::make('multiheight')
->maxLength(50)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('customparams')
->maxLength(256)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
]),
Grid::make(2)
->schema([
TextInput::make('effect_id_male')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
TextInput::make('effect_id_female')
->numeric()
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
]),
TextInput::make('clothing_on_walk')
->maxLength(255)
->nullable()
->dehydrateStateUsing(fn ($state) => $state ?? ''),
])
->action(function (array $data, $record): void {
// Transform any null or empty values to empty strings
$data = collect($data)->map(function ($value) {
if ($value === null || $value === '') {
return '';
}
if (is_bool($value)) {
return $value ? '1' : '0';
}
return $value;
})->toArray();
$record->itemBase->forceFill($data)->save();
})
->visible(fn ($record) => $record->itemBase !== null),
DeleteAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}
@@ -1,96 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\ChatlogPrivates;
use App\Filament\Resources\Hotel\ChatlogPrivates\Pages\ManageChatlogPrivates;
use App\Filament\Traits\TranslatableResource;
use App\Models\ChatlogPrivate;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ChatlogPrivateResource extends Resource
{
use TranslatableResource;
protected static ?string $model = ChatlogPrivate::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-chat-bubble-left-right';
protected static string|\UnitEnum|null $navigationGroup = 'Logs';
public static string $translateIdentifier = 'chatlog-private';
protected static ?string $slug = 'hotel/chatlog-private';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('sender')
->disabled()
->formatStateUsing(fn ($record) => $record->sender?->username)
->label(__('filament::resources.inputs.sender')),
TextInput::make('receiver')
->disabled()
->formatStateUsing(fn ($record) => $record->receiver?->username)
->label(__('filament::resources.inputs.receiver')),
Textarea::make('message')
->label(__('filament::resources.inputs.message'))
->columnSpanFull()
->disabled(),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('timestamp', 'desc')
->columns(self::getTable())
->filters([])
->recordActions([
ViewAction::make(),
])
->toolbarActions([]);
}
public static function getTable(): array
{
return [
TextColumn::make('sender.username')
->label(__('filament::resources.columns.sender'))
->toggleable()
->searchable(isIndividual: true),
TextColumn::make('receiver.username')
->label(__('filament::resources.columns.receiver'))
->toggleable()
->searchable(isIndividual: true),
TextColumn::make('message')
->label(__('filament::resources.columns.message'))
->limit(40)
->searchable(isIndividual: true),
TextColumn::make('timestamp')
->label(__('filament::resources.columns.executed_at'))
->dateTime('Y-m-d H:i')
->toggleable(),
];
}
public static function getPages(): array
{
return [
'index' => ManageChatlogPrivates::route('/'),
];
}
}
@@ -1,16 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\ChatlogPrivates\Pages;
use App\Filament\Resources\Hotel\ChatlogPrivates\ChatlogPrivateResource;
use Filament\Resources\Pages\ManageRecords;
class ManageChatlogPrivates extends ManageRecords
{
protected static string $resource = ChatlogPrivateResource::class;
protected function getHeaderActions(): array
{
return [];
}
}
@@ -1,109 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\ChatlogRooms;
use App\Filament\Resources\Hotel\ChatlogRooms\Pages\ManageChatlogRooms;
use App\Filament\Traits\TranslatableResource;
use App\Models\ChatlogRoom;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class ChatlogRoomResource extends Resource
{
use TranslatableResource;
protected static ?string $model = ChatlogRoom::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-chat-bubble-left-right';
protected static string|\UnitEnum|null $navigationGroup = 'Logs';
public static string $translateIdentifier = 'chatlog-rooms';
protected static ?string $slug = 'hotel/chatlog-room';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('room')
->label(__('filament::resources.inputs.room'))
->formatStateUsing(fn ($record) => $record->room?->name)
->columnSpanFull()
->disabled(),
TextInput::make('sender')
->label(__('filament::resources.inputs.sender'))
->formatStateUsing(fn ($record) => $record->sender?->username)
->disabled(),
TextInput::make('receiver')
->label(__('filament::resources.inputs.receiver'))
->formatStateUsing(fn ($record) => $record->receiver?->username)
->disabled(),
Textarea::make('message')
->label(__('filament::resources.inputs.message'))
->columnSpanFull()
->disabled(),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('timestamp', 'desc')
->columns(self::getTable())
->filters([
//
])
->recordActions([
ViewAction::make(),
])
->toolbarActions([]);
}
public static function getTable(): array
{
return [
TextColumn::make('room.name')
->label(__('filament::resources.columns.room'))
->toggleable()
->searchable(isIndividual: true),
TextColumn::make('sender.username')
->label(__('filament::resources.columns.sender'))
->toggleable()
->searchable(isIndividual: true),
TextColumn::make('receiver.username')
->label(__('filament::resources.columns.receiver'))
->toggleable()
->searchable(isIndividual: true),
TextColumn::make('message')
->label(__('filament::resources.columns.message'))
->limit(40)
->searchable(isIndividual: true),
TextColumn::make('timestamp')
->label(__('filament::resources.columns.executed_at'))
->dateTime('Y-m-d H:i')
->toggleable(),
];
}
public static function getPages(): array
{
return [
'index' => ManageChatlogRooms::route('/'),
];
}
}
@@ -1,16 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\ChatlogRooms\Pages;
use App\Filament\Resources\Hotel\ChatlogRooms\ChatlogRoomResource;
use Filament\Resources\Pages\ManageRecords;
class ManageChatlogRooms extends ManageRecords
{
protected static string $resource = ChatlogRoomResource::class;
protected function getHeaderActions(): array
{
return [];
}
}
@@ -1,80 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CommandLogs;
use App\Filament\Resources\Hotel\CommandLogs\Pages\ManageCommandLogs;
use App\Filament\Traits\TranslatableResource;
use App\Models\CommandLog;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
class CommandLogResource extends Resource
{
use TranslatableResource;
protected static ?string $model = CommandLog::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-chat-bubble-bottom-center-text';
protected static string|\UnitEnum|null $navigationGroup = 'Logs';
public static string $translateIdentifier = 'command-logs';
protected static ?string $slug = 'logs/commands';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema->components([]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->defaultSort('timestamp', 'desc')
->columns([
TextColumn::make('user.username')
->label(__('filament::resources.columns.username'))
->searchable(),
TextColumn::make('command')
->label(__('filament::resources.columns.command'))
->searchable(),
TextColumn::make('succes')
->badge()
->color(fn (string $state): string => match ($state) {
'yes' => 'primary',
'no' => 'warning'
})
->label(__('filament::resources.columns.success'))
->formatStateUsing(fn (string $state): string => __("filament::resources.options.{$state}")),
TextColumn::make('timestamp')
->label(__('filament::resources.columns.executed_at'))
->dateTime('Y-m-d H:i')
->searchable(),
])
->filters([
SelectFilter::make('succes')
->label(__('filament::resources.filters.success'))
->options([
'yes' => __('filament::resources.options.yes'),
'no' => __('filament::resources.options.no'),
]),
])
->recordActions([])
->toolbarActions([]);
}
public static function getPages(): array
{
return [
'index' => ManageCommandLogs::route('/'),
];
}
}
@@ -1,21 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\CommandLogs\Pages;
use App\Filament\Resources\Hotel\CommandLogs\CommandLogResource;
use Filament\Resources\Pages\ManageRecords;
class ManageCommandLogs extends ManageRecords
{
protected static string $resource = CommandLogResource::class;
protected function getActions(): array
{
return [];
}
public function getPrimaryKey(): string
{
return 'timestamp';
}
}
@@ -1,21 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class CustomQueryBuilder extends Builder
{
public function __construct()
{
// Call the parent constructor with a dummy query
parent::__construct(app('db')->query());
}
#[\Override]
public function get($columns = ['*']): Collection
{
return collect(); // Return an empty collection
}
}
@@ -1,93 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\EmulatorSettings;
use App\Filament\Resources\Hotel\EmulatorSettings\Pages\CreateEmulatorSetting;
use App\Filament\Resources\Hotel\EmulatorSettings\Pages\EditEmulatorSetting;
use App\Filament\Resources\Hotel\EmulatorSettings\Pages\ListEmulatorSettings;
use App\Filament\Traits\TranslatableResource;
use App\Models\EmulatorSetting;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class EmulatorSettingResource extends Resource
{
use TranslatableResource;
protected static ?string $model = EmulatorSetting::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-adjustments-horizontal';
protected static string|\UnitEnum|null $navigationGroup = 'Hotel';
public static string $translateIdentifier = 'emulator-settings';
protected static ?string $slug = 'hotel/emulator-settings';
#[\Override]
public static function form(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
TextInput::make('key')
->label(__('filament::resources.inputs.key'))
->required()
->maxLength(100)
->unique(ignoreRecord: true),
TextInput::make('value')
->label(__('filament::resources.inputs.value'))
->required()
->maxLength(512),
]),
]);
}
#[\Override]
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('key')
->label(__('filament::resources.columns.key'))
->searchable(),
TextColumn::make('value')
->label(__('filament::resources.columns.value'))
->searchable(),
])
->filters([
//
])
->recordActions([
ViewAction::make(),
EditAction::make(),
])
->toolbarActions([]);
}
#[\Override]
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListEmulatorSettings::route('/'),
'create' => CreateEmulatorSetting::route('/create'),
'edit' => EditEmulatorSetting::route('/{record}/edit'),
];
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\EmulatorSettings\Pages;
use App\Filament\Resources\Hotel\EmulatorSettings\EmulatorSettingResource;
use Filament\Resources\Pages\CreateRecord;
class CreateEmulatorSetting extends CreateRecord
{
protected static string $resource = EmulatorSettingResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\EmulatorSettings\Pages;
use App\Filament\Resources\Hotel\EmulatorSettings\EmulatorSettingResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditEmulatorSetting extends EditRecord
{
protected static string $resource = EmulatorSettingResource::class;
protected function getActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\Hotel\EmulatorSettings\Pages;
use App\Filament\Resources\Hotel\EmulatorSettings\EmulatorSettingResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListEmulatorSettings extends ListRecords
{
protected static string $resource = EmulatorSettingResource::class;
protected function getActions(): array
{
return [
CreateAction::make(),
];
}
}

Some files were not shown because too many files have changed in this diff Show More