Compare commits
	
		
			8 Commits
		
	
	
		
			e78324e92a
			...
			3f340d57fc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						3f340d57fc
	
				 | 
					
					
						|||
| 
						
						
							
						
						995cc32578
	
				 | 
					
					
						|||
| 
						
						
							
						
						5f5b443df7
	
				 | 
					
					
						|||
| 
						
						
							
						
						e5366171fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						1c650fbb64
	
				 | 
					
					
						|||
| 
						
						
							
						
						a784d44d16
	
				 | 
					
					
						|||
| 
						
						
							
						
						e27aa8969f
	
				 | 
					
					
						|||
| 
						
						
							
						
						433ad39a08
	
				 | 
					
					
						
@@ -4,7 +4,7 @@ APP_KEY=base64:hSCTwZ507IdKQ5QJHJ+mQw0DSMgDdAspasjwHCdiB8Y=
 | 
				
			|||||||
APP_DEBUG=true
 | 
					APP_DEBUG=true
 | 
				
			||||||
APP_DOMAIN=localhost
 | 
					APP_DOMAIN=localhost
 | 
				
			||||||
APP_URL="https://${APP_DOMAIN}"
 | 
					APP_URL="https://${APP_DOMAIN}"
 | 
				
			||||||
APP_UID_BYTES=8
 | 
					ADMIN_EMAIL=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GIT_HASH="00000000"
 | 
					GIT_HASH="00000000"
 | 
				
			||||||
GIT_TAG="x.x.x"
 | 
					GIT_TAG="x.x.x"
 | 
				
			||||||
@@ -28,6 +28,9 @@ FILESYSTEM_DRIVER=local
 | 
				
			|||||||
QUEUE_CONNECTION=sync
 | 
					QUEUE_CONNECTION=sync
 | 
				
			||||||
SESSION_DRIVER=database
 | 
					SESSION_DRIVER=database
 | 
				
			||||||
SESSION_LIFETIME=120
 | 
					SESSION_LIFETIME=120
 | 
				
			||||||
 | 
					#SESSION_STORE=redis
 | 
				
			||||||
 | 
					#SESSION_DOMAIN="${APP_DOMAIN}"
 | 
				
			||||||
 | 
					#SESSION_SECURE_COOKIE=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MEMCACHED_HOST=memcache
 | 
					MEMCACHED_HOST=memcache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,6 +38,15 @@ REDIS_HOST=redis
 | 
				
			|||||||
REDIS_PASSWORD=null
 | 
					REDIS_PASSWORD=null
 | 
				
			||||||
REDIS_PORT=6379
 | 
					REDIS_PORT=6379
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAIL_MAILER=smtp
 | 
				
			||||||
 | 
					MAIL_HOST=mailhog
 | 
				
			||||||
 | 
					MAIL_PORT=1125
 | 
				
			||||||
 | 
					MAIL_USERNAME=null
 | 
				
			||||||
 | 
					MAIL_PASSWORD=null
 | 
				
			||||||
 | 
					MAIL_ENCRYPTION=null
 | 
				
			||||||
 | 
					MAIL_FROM_ADDRESS="hello@${APP_DOMAIN}"
 | 
				
			||||||
 | 
					MAIL_FROM_NAME="${APP_NAME}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SCOUT_DRIVER=meilisearch
 | 
					SCOUT_DRIVER=meilisearch
 | 
				
			||||||
SCOUT_PREFIX=
 | 
					SCOUT_PREFIX=
 | 
				
			||||||
SCOUT_QUEUE=false
 | 
					SCOUT_QUEUE=false
 | 
				
			||||||
@@ -43,14 +55,15 @@ MEILISEARCH_KEY=
 | 
				
			|||||||
MEILISEARCH_PRIVATE_KEY=
 | 
					MEILISEARCH_PRIVATE_KEY=
 | 
				
			||||||
MEILISEARCH_PUBLIC_KEY=
 | 
					MEILISEARCH_PUBLIC_KEY=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MAIL_MAILER=smtp
 | 
					GOOGLE_GEOCODE_API_KEY=
 | 
				
			||||||
MAIL_HOST=mailhog
 | 
					
 | 
				
			||||||
MAIL_PORT=1125
 | 
					MINIO_USERNAME=
 | 
				
			||||||
MAIL_USERNAME=null
 | 
					MINIO_PASSWORD=
 | 
				
			||||||
MAIL_PASSWORD=null
 | 
					MINIO_DEFAULT_REGION=us-west-1
 | 
				
			||||||
MAIL_ENCRYPTION=null
 | 
					MINIO_BUCKET=
 | 
				
			||||||
MAIL_FROM_ADDRESS="no-reply@${APP_DOMAIN}"
 | 
					MINIO_URL=
 | 
				
			||||||
MAIL_FROM_NAME="${APP_NAME}"
 | 
					MINIO_ENDPOINT=
 | 
				
			||||||
 | 
					MINIO_USE_PATH_STYLE_ENDPOINT=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AWS_ACCESS_KEY_ID=
 | 
					AWS_ACCESS_KEY_ID=
 | 
				
			||||||
AWS_SECRET_ACCESS_KEY=
 | 
					AWS_SECRET_ACCESS_KEY=
 | 
				
			||||||
@@ -84,3 +97,13 @@ INTEGRITY_HASH_WEBMANIFEST_JSON=""
 | 
				
			|||||||
INTEGRITY_HASH_MIX_MANIFEST_JSON=""
 | 
					INTEGRITY_HASH_MIX_MANIFEST_JSON=""
 | 
				
			||||||
INTEGRITY_HASH_APP_CSS=""
 | 
					INTEGRITY_HASH_APP_CSS=""
 | 
				
			||||||
INTEGRITY_HASH_APP_JS=""
 | 
					INTEGRITY_HASH_APP_JS=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Clockwork debug helpers
 | 
				
			||||||
 | 
					## default values are set, except for the main enable switch
 | 
				
			||||||
 | 
					CLOCKWORK_ENABLE=true
 | 
				
			||||||
 | 
					CLOCKWORK_WEB=true
 | 
				
			||||||
 | 
					CLOCKWORK_AUTHENTICATION=false
 | 
				
			||||||
 | 
					#CLOCKWORK_AUTHENTICATION_PASSWORD=VerySecretPassword
 | 
				
			||||||
 | 
					CLOCKWORK_CACHE_COLLECT_VALUES=true
 | 
				
			||||||
 | 
					#CLOCKWORK_DATABASE_SLOW_THRESHOLD= # time in miliseconds
 | 
				
			||||||
 | 
					#CLOCKWORK_REQUESTS_SLOW_THRESHOLD= # time in miliseconds
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								src/app/Console/Commands/TranslationCheckerCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/app/Console/Commands/TranslationCheckerCommand.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TranslationCheckerCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The name and signature of the console command.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $signature = 'i18n:check-json {--l|locale= : The source of truth locale}';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The console command description.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $description = 'Uses one JSON file as the source of truth to check other lang files against.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the console command.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $masterLocale = 'en';
 | 
				
			||||||
 | 
					        if (!empty($this->option('locale'))) {
 | 
				
			||||||
 | 
					            $masterLocale = trim($this->option('locale'));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $langDir = base_path('lang');
 | 
				
			||||||
 | 
					        $langFiles = scandir($langDir);
 | 
				
			||||||
 | 
					        $bigCount = count($langFiles);
 | 
				
			||||||
 | 
					        for ($i = 0; $i < $bigCount; $i++) {
 | 
				
			||||||
 | 
					            if (! preg_match('/.*\.json$/', $langFiles[$i]) || "{$masterLocale}.json" === $langFiles[$i]) {
 | 
				
			||||||
 | 
					                unset($langFiles[$i]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $langFilesCount = count($langFiles);
 | 
				
			||||||
 | 
					        $this->info("Checking {$langFilesCount} file(s) against {$masterLocale}...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $masterLocaleTranslations = translations(base_path("lang/{$masterLocale}.json"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($langFiles as $localeFile) {
 | 
				
			||||||
 | 
					            $otherLocaleTranslations = translations(base_path("lang/{$localeFile}"));
 | 
				
			||||||
 | 
					            $counts = 0;
 | 
				
			||||||
 | 
					            $mergedTranslations = $this->recursiveMergeArray($masterLocaleTranslations, $otherLocaleTranslations, $counts);
 | 
				
			||||||
 | 
					            file_put_contents(base_path("lang/{$localeFile}"), json_encode($mergedTranslations, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT));
 | 
				
			||||||
 | 
					            $this->info("{$localeFile} had {$counts} missing translations.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Command::SUCCESS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function recursiveMergeArray(array $firstArray, array $secondArray, int &$counts)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $mergedArray = $secondArray;
 | 
				
			||||||
 | 
					        foreach ($firstArray as $key => $value) {
 | 
				
			||||||
 | 
					            if (is_array($value)) {
 | 
				
			||||||
 | 
					                if (!array_key_exists($key, $secondArray)) {
 | 
				
			||||||
 | 
					                    $mergedArray[$key] = $value;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $subDiff = $this->recursiveMergeArray($value, $secondArray[$key], $counts);
 | 
				
			||||||
 | 
					                    if (!empty($subDiff)) {
 | 
				
			||||||
 | 
					                        $mergedArray[$key] = $subDiff;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (!array_key_exists($key, $secondArray)) {
 | 
				
			||||||
 | 
					                    $counts++;
 | 
				
			||||||
 | 
					                    $mergedArray[$key] = $value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $mergedArray;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/app/Http/Kernel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/app/Http/Kernel.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Http\Kernel as HttpKernel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Kernel extends HttpKernel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    //...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The application's route middleware groups.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var array<string, array<int, class-string|string>>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $middlewareGroups = [
 | 
				
			||||||
 | 
					        'web' => [
 | 
				
			||||||
 | 
					            //...
 | 
				
			||||||
 | 
					            \Illuminate\Session\Middleware\StartSession::class,
 | 
				
			||||||
 | 
					            \App\Http\Middleware\SetLocale::class,
 | 
				
			||||||
 | 
					            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
 | 
				
			||||||
 | 
					            //...
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'admin' => [
 | 
				
			||||||
 | 
					            //...
 | 
				
			||||||
 | 
					            \Laravel\Jetstream\Http\Middleware\ShareInertiaData::class,
 | 
				
			||||||
 | 
					            \App\Http\Middleware\SetLocale::class,
 | 
				
			||||||
 | 
					            \App\Http\Middleware\AuthorizeAdmin::class,
 | 
				
			||||||
 | 
					            //...
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,22 +5,20 @@ namespace App\Http\Middleware;
 | 
				
			|||||||
use Closure;
 | 
					use Closure;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CheckCustomSessionData
 | 
					class AuthorizeAdmin
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handle an incoming request.
 | 
					     * Handle an incoming request.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @since 1.0.0
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param  \Illuminate\Http\Request  $request
 | 
					     * @param  \Illuminate\Http\Request  $request
 | 
				
			||||||
     * @param  \Closure  $next
 | 
					     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return mixed
 | 
					     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle(Request $request, Closure $next)
 | 
					    public function handle(Request $request, Closure $next)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ((! session()->has('thing') || empty(session('thing'))) && $request->user()) {
 | 
					        if ($request->user()->email !== env('ADMIN_EMAIL')) {
 | 
				
			||||||
            session()->put('thing', $request->user()->thing);
 | 
					            abort(HTTP_NOT_FOUND);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $next($request);
 | 
					        return $next($request);
 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Http\Middleware;
 | 
					namespace App\Http\Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Language;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Inertia\Middleware;
 | 
					use Inertia\Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,19 +42,56 @@ class HandleInertiaRequests extends Middleware
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function share(Request $request): array
 | 
					    public function share(Request $request): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $localeFields = ['locale', 'iso_code', 'name', 'localized_name'];
 | 
				
			||||||
 | 
					        $currentLocale = $request->session()->get('locale', null);
 | 
				
			||||||
 | 
					        if (is_null($currentLocale)) {
 | 
				
			||||||
 | 
					            $currentLocale = Language::where(['locale' => 'en', 'iso_code' => 'en_US'])->get($localeFields)[0]->toArray();
 | 
				
			||||||
 | 
					            $request->session()->put('locale', [
 | 
				
			||||||
 | 
					                'locale'         => $currentLocale['locale'],
 | 
				
			||||||
 | 
					                'iso_code'       => $currentLocale['iso_code'],
 | 
				
			||||||
 | 
					                'name'           => $currentLocale['name'],
 | 
				
			||||||
 | 
					                'localized_name' => $currentLocale['localized_name'],
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $localeFilePath = base_path("lang/{$currentLocale['locale']}.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $notifications = [];
 | 
					        $notifications = [];
 | 
				
			||||||
 | 
					        $notificationsCount = count($notifications);
 | 
				
			||||||
        $unreadNotifications = false;
 | 
					        $unreadNotifications = false;
 | 
				
			||||||
        if (! is_null($request->user())) {
 | 
					        if (! is_null($request->user())) {
 | 
				
			||||||
            $notifications = $request->user()->notifications;
 | 
					            $notifications = $request->user()->notifications;
 | 
				
			||||||
 | 
					            $notificationsCount = count($notifications);
 | 
				
			||||||
 | 
					            for ($i = 0; $i < $notificationsCount; $i++) {
 | 
				
			||||||
 | 
					                $newData = $notifications[$i]->data;
 | 
				
			||||||
 | 
					                $createdAt = carbon($notifications[$i]->created_at);
 | 
				
			||||||
 | 
					                $dateFormat = 'F j';
 | 
				
			||||||
 | 
					                if (!$createdAt->is(gmdate('Y'))) {
 | 
				
			||||||
 | 
					                    $dateFormat = 'F j, Y';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $newData['created_at_date'] = $createdAt->copy()->format($dateFormat);
 | 
				
			||||||
 | 
					                $newData['created_at_time'] = $createdAt->copy()->format('H:i');
 | 
				
			||||||
 | 
					                $notifications[$i]->data = $newData;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (count($request->user()->unreadNotifications) > 0) {
 | 
					            if (count($request->user()->unreadNotifications) > 0) {
 | 
				
			||||||
                $unreadNotifications = true;
 | 
					                $unreadNotifications = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return array_merge(parent::share($request), [
 | 
					        $additionalData = [
 | 
				
			||||||
            'appName'             => config('app.name'),
 | 
					            'appName' => config('app.name'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            'availableLocales' => Language::get($localeFields),
 | 
				
			||||||
 | 
					            'currentLocale'    => $currentLocale,
 | 
				
			||||||
 | 
					            'language'         => translations($localeFilePath),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            'notifications'       => $notifications,
 | 
					            'notifications'       => $notifications,
 | 
				
			||||||
            'unreadNotifications' => $unreadNotifications,
 | 
					            'unreadNotifications' => $unreadNotifications,
 | 
				
			||||||
        ]);
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($request->user()->email === env('ADMIN_EMAIL')) {
 | 
				
			||||||
 | 
					            $additionalData['is_admin_user'] = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return array_merge(parent::share($request), $additionalData);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								src/app/Http/Middleware/SetLocale.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/app/Http/Middleware/SetLocale.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Closure;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SetLocale
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle an incoming request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @package App\Http\Middleware\SetLocale
 | 
				
			||||||
 | 
					     * @since 1.0.0
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  \Illuminate\Http\Request  $request
 | 
				
			||||||
 | 
					     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle(Request $request, Closure $next)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (session()->has('locale')) {
 | 
				
			||||||
 | 
					            app()->setLocale(session('locale')['locale']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $next($request);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/app/Models/Scopes/OnTeamScope.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/app/Models/Scopes/OnTeamScope.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					namespace App\Models\Scopes;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Builder;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Model;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Scope;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					class OnTeamScope implements Scope
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Apply the scope to a given Eloquent query builder.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  \Illuminate\Database\Eloquent\Builder  $builder
 | 
				
			||||||
 | 
					     * @param  \Illuminate\Database\Eloquent\Model  $model
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function apply(Builder $builder, Model $model): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $builder->where('current_team_id', );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -37,6 +37,7 @@ class User extends Authenticatable
 | 
				
			|||||||
        'name',
 | 
					        'name',
 | 
				
			||||||
        'surname',
 | 
					        'surname',
 | 
				
			||||||
        'timezone_name',
 | 
					        'timezone_name',
 | 
				
			||||||
 | 
					        'language_id',
 | 
				
			||||||
        'current_team_id',
 | 
					        'current_team_id',
 | 
				
			||||||
        'profile_photo_path',
 | 
					        'profile_photo_path',
 | 
				
			||||||
        'email',
 | 
					        'email',
 | 
				
			||||||
@@ -183,4 +184,17 @@ class User extends Authenticatable
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphOne(Address::class, 'addressable');
 | 
					        return $this->morphOne(Address::class, 'addressable');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Language relationship.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @package App\Models\User
 | 
				
			||||||
 | 
					     * @since 1.0.0
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function language(): BelongsTo
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->belongsTo(Language::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Database\Factories;
 | 
					namespace Database\Factories;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Language;
 | 
				
			||||||
use App\Models\Team;
 | 
					use App\Models\Team;
 | 
				
			||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
use Illuminate\Database\Eloquent\Factories\Factory;
 | 
					use Illuminate\Database\Eloquent\Factories\Factory;
 | 
				
			||||||
@@ -31,6 +32,7 @@ class UserFactory extends Factory
 | 
				
			|||||||
            'email'             => $this->faker->unique()->safeEmail(),
 | 
					            'email'             => $this->faker->unique()->safeEmail(),
 | 
				
			||||||
            'email_verified_at' => now(),
 | 
					            'email_verified_at' => now(),
 | 
				
			||||||
            'timezone_name'     => $this->faker->timezone(),
 | 
					            'timezone_name'     => $this->faker->timezone(),
 | 
				
			||||||
 | 
					            'timezone_name'     => Language::all()->random()->id,
 | 
				
			||||||
            'password'          => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
 | 
					            'password'          => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
 | 
				
			||||||
            'remember_token'    => Str::random(10),
 | 
					            'remember_token'    => Str::random(10),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,49 +16,18 @@ class LanguageSeeder extends Seeder
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function run()
 | 
					    public function run()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $languages = [
 | 
					        $languagesData = [
 | 
				
			||||||
            [
 | 
					            ['locale' => 'en', 'iso_code' => 'en_US', 'name' => 'English', 'localized_name' => 'English (US)'],
 | 
				
			||||||
                'iso_code'        => 'en_US',
 | 
					            ['locale' => 'de', 'iso_code' => 'de_DE', 'name' => 'German', 'localized_name' => 'Deutsch'],
 | 
				
			||||||
                'locale'          => 'en',
 | 
					            ['locale' => 'fr', 'iso_code' => 'fr_FR', 'name' => 'French', 'localized_name' => 'Français'],
 | 
				
			||||||
                'title'           => 'English',
 | 
					            ['locale' => 'jp', 'iso_code' => 'jp_JP', 'name' => 'Japanese', 'localized_name' => '日本'],
 | 
				
			||||||
                'title_localized' => 'English',
 | 
					            ['locale' => 'mx', 'iso_code' => 'es_MX', 'name' => 'Spanish', 'localized_name' => 'Español'],
 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                'iso_code'        => 'de_DE',
 | 
					 | 
				
			||||||
                'locale'          => 'de',
 | 
					 | 
				
			||||||
                'title'           => 'German',
 | 
					 | 
				
			||||||
                'title_localized' => 'Deutsch',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                'iso_code'        => 'fr_FR',
 | 
					 | 
				
			||||||
                'locale'          => 'fr',
 | 
					 | 
				
			||||||
                'title'           => 'French',
 | 
					 | 
				
			||||||
                'title_localized' => 'Français',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                'iso_code'        => 'es_SP',
 | 
					 | 
				
			||||||
                'locale'          => 'es',
 | 
					 | 
				
			||||||
                'title'           => 'Spanish',
 | 
					 | 
				
			||||||
                'title_localized' => 'Español',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                'iso_code'        => 'jp_JP',
 | 
					 | 
				
			||||||
                'locale'          => 'jp',
 | 
					 | 
				
			||||||
                'title'           => 'Japanese',
 | 
					 | 
				
			||||||
                'title_localized' => '日本',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                'iso_code'        => 'zh_TW',
 | 
					 | 
				
			||||||
                'locale'          => 'zh',
 | 
					 | 
				
			||||||
                'title'           => 'Taiwanese',
 | 
					 | 
				
			||||||
                'title_localized' => '台湾',
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $startTime = Carbon::now();
 | 
					        $startTime = Carbon::now();
 | 
				
			||||||
        $offset = 0;
 | 
					        $offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($languages as $language) {
 | 
					        foreach ($languagesData as $language) {
 | 
				
			||||||
            $datetime = $startTime->copy()->addMinute($offset)->toDateTimeString();
 | 
					            $datetime = $startTime->copy()->addMinute($offset)->toDateTimeString();
 | 
				
			||||||
            $offset++;
 | 
					            $offset++;
 | 
				
			||||||
            $language['created_at'] = $datetime;
 | 
					            $language['created_at'] = $datetime;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,88 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "key": "translation that has :attribute in the string."
 | 
					    "titles": {
 | 
				
			||||||
 | 
					        "welcome": "Welcome",
 | 
				
			||||||
 | 
					        "dashboard": "Dashboard",
 | 
				
			||||||
 | 
					        "secure_area": "Secure Area",
 | 
				
			||||||
 | 
					        "forgot_password": "Forgot Password",
 | 
				
			||||||
 | 
					        "my_profile": "My Profile",
 | 
				
			||||||
 | 
					        "edit_profile": "Edit Profile",
 | 
				
			||||||
 | 
					        "log_in": "Log In To Your Account",
 | 
				
			||||||
 | 
					        "register": "Register A New Account",
 | 
				
			||||||
 | 
					        "reset_password": "Reset Password",
 | 
				
			||||||
 | 
					        "two_factor_confirmation": "Two-factor Confirmation",
 | 
				
			||||||
 | 
					        "verify_email": "Email Verification"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "labels": {
 | 
				
			||||||
 | 
					        "type": "Type",
 | 
				
			||||||
 | 
					        "none": "None",
 | 
				
			||||||
 | 
					        "title": "Title",
 | 
				
			||||||
 | 
					        "name": "Name",
 | 
				
			||||||
 | 
					        "created_at": "Created At",
 | 
				
			||||||
 | 
					        "updated_at": "Updated At",
 | 
				
			||||||
 | 
					        "actions": "Actions",
 | 
				
			||||||
 | 
					        "email": "Email",
 | 
				
			||||||
 | 
					        "timezone_name": "Timezone",
 | 
				
			||||||
 | 
					        "language": "Language",
 | 
				
			||||||
 | 
					        "password": "Password",
 | 
				
			||||||
 | 
					        "confirm_password": "Confirm Password",
 | 
				
			||||||
 | 
					        "current_password": "Current Password",
 | 
				
			||||||
 | 
					        "remember_me": "Remember me",
 | 
				
			||||||
 | 
					        "first_name": "First Name",
 | 
				
			||||||
 | 
					        "last_name": "Surname",
 | 
				
			||||||
 | 
					        "forgot_password": "Forgot your password?",
 | 
				
			||||||
 | 
					        "already_registered": "Already registered?",
 | 
				
			||||||
 | 
					        "code": "Code",
 | 
				
			||||||
 | 
					        "recovery_code": "Recovery Code",
 | 
				
			||||||
 | 
					        "use_recovery_code": "Use a recovery code",
 | 
				
			||||||
 | 
					        "use_authentication_code": "Use an authentication code",
 | 
				
			||||||
 | 
					        "email_verification": "Email Verification",
 | 
				
			||||||
 | 
					        "user": "User",
 | 
				
			||||||
 | 
					        "optional": "optional"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "actions": {
 | 
				
			||||||
 | 
					        "save": "Save",
 | 
				
			||||||
 | 
					        "update": "Update",
 | 
				
			||||||
 | 
					        "edit": "Edit",
 | 
				
			||||||
 | 
					        "delete": "Delete",
 | 
				
			||||||
 | 
					        "confirm": "Confirm",
 | 
				
			||||||
 | 
					        "log_in": "Log in",
 | 
				
			||||||
 | 
					        "log_out": "Log Out",
 | 
				
			||||||
 | 
					        "register": "Register",
 | 
				
			||||||
 | 
					        "edit_profile": "Edit Profile",
 | 
				
			||||||
 | 
					        "confirm_password": "Confirm Password",
 | 
				
			||||||
 | 
					        "reset_password": "Reset Password",
 | 
				
			||||||
 | 
					        "email_password_reset_link": "Email Password Reset Link",
 | 
				
			||||||
 | 
					        "resend_verification_email": "Resend Verification Email"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "nav": {
 | 
				
			||||||
 | 
					        "manage_team": "Manage Team",
 | 
				
			||||||
 | 
					        "team_settings": "Team Settings",
 | 
				
			||||||
 | 
					        "create_new_team": "Create New Team",
 | 
				
			||||||
 | 
					        "switch_teams": "Switch Teams",
 | 
				
			||||||
 | 
					        "manage_account": "Manage Account",
 | 
				
			||||||
 | 
					        "profile": "My Profile",
 | 
				
			||||||
 | 
					        "admin_settings": "Admin Settings",
 | 
				
			||||||
 | 
					        "api_tokens": "API Tokens"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "pagination": {
 | 
				
			||||||
 | 
					        "show_from_to_count": "Showing :from to :to of :count results"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "notifications": {
 | 
				
			||||||
 | 
					        "saved": "Saved.",
 | 
				
			||||||
 | 
					        "verified": "Verified!",
 | 
				
			||||||
 | 
					        "unverified": "Unverified.",
 | 
				
			||||||
 | 
					        "new_email_verification_sent": "A new verification link has been sent to the email address you provided in your profile settings."
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "pages": {
 | 
				
			||||||
 | 
					        "profile": {
 | 
				
			||||||
 | 
					            "titles": {
 | 
				
			||||||
 | 
					                "update_password": "Update Your Password"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "text_blocks": {
 | 
				
			||||||
 | 
					                "unverified_message": "Please verify your account.",
 | 
				
			||||||
 | 
					                "verified_message": "Thanks for being amazing.",
 | 
				
			||||||
 | 
					                "secure_password": "Ensure your account is using a long, random password to stay secure."
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,28 +1,24 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "private": true,
 | 
					    "private": true,
 | 
				
			||||||
    "scripts": {
 | 
					    "scripts": {
 | 
				
			||||||
        "dev": "npm run development",
 | 
					        "dev": "vite",
 | 
				
			||||||
        "development": "mix",
 | 
					        "build": "vite build"
 | 
				
			||||||
        "watch": "mix watch",
 | 
					 | 
				
			||||||
        "watch-poll": "mix watch -- --watch-options-poll=1000",
 | 
					 | 
				
			||||||
        "hot": "mix watch --hot",
 | 
					 | 
				
			||||||
        "prod": "npm run production",
 | 
					 | 
				
			||||||
        "production": "mix --production"
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "laravel-mix": "^6.0.49",
 | 
					        "@vitejs/plugin-vue": "^3.0.0",
 | 
				
			||||||
 | 
					        "laravel-vite-plugin": "^0.6.0",
 | 
				
			||||||
        "postcss": "^8.4.14",
 | 
					        "postcss": "^8.4.14",
 | 
				
			||||||
        "postcss-import": "^14.1.0",
 | 
					        "vite": "^3.0.0"
 | 
				
			||||||
        "vue-loader": "^17.0.0"
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "@inertiajs/inertia": "^0.11.0",
 | 
					        "@inertiajs/inertia": "^0.11.0",
 | 
				
			||||||
        "@inertiajs/inertia-vue3": "^0.6.0",
 | 
					        "@inertiajs/inertia-vue3": "^0.6.0",
 | 
				
			||||||
        "@inertiajs/progress": "^0.2.7",
 | 
					        "@inertiajs/progress": "^0.2.7",
 | 
				
			||||||
        "@tailwindcss/aspect-ratio": "^0.4.0",
 | 
					        "@tailwindcss/aspect-ratio": "^0.4.0",
 | 
				
			||||||
        "@tailwindcss/forms": "^0.5.2",
 | 
					        "@tailwindcss/line-clamp": "^0.4.2",
 | 
				
			||||||
        "@tailwindcss/typography": "^0.5.2",
 | 
					        "@tailwindcss/typography": "^0.5.2",
 | 
				
			||||||
        "@vueuse/core": "^8.7.5",
 | 
					        "@vueuse/core": "^8.7.5",
 | 
				
			||||||
 | 
					        "apexcharts": "^3.35.5",
 | 
				
			||||||
        "axios": "^0.27.2",
 | 
					        "axios": "^0.27.2",
 | 
				
			||||||
        "daisyui": "^2.29.0",
 | 
					        "daisyui": "^2.29.0",
 | 
				
			||||||
        "dayjs": "^1.11.3",
 | 
					        "dayjs": "^1.11.3",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								src/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					    plugins: {
 | 
				
			||||||
 | 
					        tailwindcss: {},
 | 
				
			||||||
 | 
					        autoprefixer: {},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										59
									
								
								src/resources/css/animations/bubbleup.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/resources/css/animations/bubbleup.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					<div class="bubbles-container">
 | 
				
			||||||
 | 
					    <div class="bubbles">
 | 
				
			||||||
 | 
					        <span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
 | 
				
			||||||
 | 
					        <span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
 | 
				
			||||||
 | 
					        <span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
 | 
				
			||||||
 | 
					        <span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
 | 
				
			||||||
 | 
					        <span class="animate-bubble" style="style="--i:(random value between 1 and 15);"></span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					**/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bubbles-container {
 | 
				
			||||||
 | 
					    background: hsl(216, 57.1%, 11%);
 | 
				
			||||||
 | 
					    min-height: 100%;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bubbles {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bubbles span {
 | 
				
			||||||
 | 
					    border-radius: 50%;
 | 
				
			||||||
 | 
					    height: 30px;
 | 
				
			||||||
 | 
					    margin: 0 4px;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    width: 30px;
 | 
				
			||||||
 | 
					    /*animation: bubble 15s linear infinite;*/
 | 
				
			||||||
 | 
					    /*animation-name: bubbleup;
 | 
				
			||||||
 | 
					    animation-timing-function: linear;
 | 
				
			||||||
 | 
					    animation-iteration-count: infinite;
 | 
				
			||||||
 | 
					    animation-duration: calc(300s / calc(var(--i) * 5));*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bubbles span:nth-child(odd) {
 | 
				
			||||||
 | 
					    background: hsl(191, 66.8%, 58.6%);
 | 
				
			||||||
 | 
					    box-shadow: 0 0 0 10px hsla(191, 66.8%, 58.6%, 0.3), 0 0 50px hsl(191, 66.8%, 58.6%), 0 0 100px hsl(191, 66.8%, 58.6%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.bubbles span:nth-child(even) {
 | 
				
			||||||
 | 
					    background: hsl(339, 100%, 58.8%);
 | 
				
			||||||
 | 
					    box-shadow: 0 0 0 10px hsla(339, 100%, 58.8%, 0.3), 0 0 50px hsl(339, 100%, 58.8%), 0 0 100px hsl(339, 100%, 58.8%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.animate-bubble {
 | 
				
			||||||
 | 
					    animation-name: bubbleup;
 | 
				
			||||||
 | 
					    animation-duration: calc(300s / calc(var(--i) * 5));
 | 
				
			||||||
 | 
					    animation-timing-function: linear;
 | 
				
			||||||
 | 
					    animation-iteration-count: infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes bubbleup {
 | 
				
			||||||
 | 
					    0% { transform: translateY(100px) scale(0); }
 | 
				
			||||||
 | 
					    100% { transform: translateY(-100px) scale(1); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/resources/js/Components/BellNotifications.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/resources/js/Components/BellNotifications.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { ref } from 'vue'
 | 
				
			||||||
 | 
					import { usePage } from '@inertiajs/inertia-vue3'
 | 
				
			||||||
 | 
					import Dropdown from './Dropdown.vue'
 | 
				
			||||||
 | 
					import GhostButton from '@/Components/Buttons/GhostButton.vue'
 | 
				
			||||||
 | 
					import IconBell from '@/Icons/Bell.vue'
 | 
				
			||||||
 | 
					import Modal from '@/Components/Modals/Modal.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// variables
 | 
				
			||||||
 | 
					const notifications = ref(usePage().props.value.notifications)
 | 
				
			||||||
 | 
					const hasUnreadNotifications = ref(usePage().props.value.unreadNotifications)
 | 
				
			||||||
 | 
					const showNotification = ref(false)
 | 
				
			||||||
 | 
					const activeNotification = ref({})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// methods
 | 
				
			||||||
 | 
					const openNotification = (notification) => {
 | 
				
			||||||
 | 
					  activeNotification.value = notification
 | 
				
			||||||
 | 
					  showNotification.value = true
 | 
				
			||||||
 | 
					  markActiveAsRead()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const closeModal = () => {
 | 
				
			||||||
 | 
					  showNotification.value = false
 | 
				
			||||||
 | 
					  activeNotification.value = {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const markActiveAsRead = () => {
 | 
				
			||||||
 | 
					  axios.post(route('api.notifications.mark-read', activeNotification.value.id))
 | 
				
			||||||
 | 
					    .then(resp => {
 | 
				
			||||||
 | 
					      activeNotification.value.read_at = resp.data.read_at
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .catch(erro => {
 | 
				
			||||||
 | 
					//console.error(erro)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const markAllRead = () => {
 | 
				
			||||||
 | 
					  axios.post(route('api.notifications.mark-all-read'))
 | 
				
			||||||
 | 
					    .then(resp => {
 | 
				
			||||||
 | 
					      let nowDt = new Date()
 | 
				
			||||||
 | 
					      notifications.value.forEach(notification => {
 | 
				
			||||||
 | 
					        if (notification.read_at === null) {
 | 
				
			||||||
 | 
					          notification.read_at = nowDt
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      hasUnreadNotifications.value = false
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .catch(erro => {
 | 
				
			||||||
 | 
					//console.error(erro)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <Dropdown align="right" width="80">
 | 
				
			||||||
 | 
					        <template #trigger>
 | 
				
			||||||
 | 
					            <GhostButton>
 | 
				
			||||||
 | 
					                <IconBell></IconBell>
 | 
				
			||||||
 | 
					                <div v-show="hasUnreadNotifications" class="absolute top-2 right-4 w-3 h-3 z-10 border-2 border-stone-100 bg-red-800 rounded-full"> </div>
 | 
				
			||||||
 | 
					            </GhostButton>
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <template #content>
 | 
				
			||||||
 | 
					            <div class="w-full">
 | 
				
			||||||
 | 
					                <div class="grid auto-rows-max gap-y-px bg-stone-400">
 | 
				
			||||||
 | 
					                    <template v-for="notification in notifications" :key="notification.id">
 | 
				
			||||||
 | 
					                    <div class="px-4 py-2 grid auto-rows-max gap-3 cursor-pointer" :class="{'bg-stone-200': notification.read_at !== null, 'bg-white': notification.read_at === null}" @click="openNotification(notification)">
 | 
				
			||||||
 | 
					                        <div>{{ notification.data.title }}</div>
 | 
				
			||||||
 | 
					                        <div class="text-xs text-zinc-600">{{ notification.data.created_at_date }}</div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <div class="border-t border-gray-100"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <div class="px-2">
 | 
				
			||||||
 | 
					                    <button class="btn btn-ghost text-xs" @click="markAllRead">Mark All Read</button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					    </Dropdown>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Modal :show="showNotification" @close="closeModal">
 | 
				
			||||||
 | 
					        <div class="grid auto-rows-max">
 | 
				
			||||||
 | 
					            <header class="bg-sky-800 text-white p-4">{{ activeNotification.data.title }}</header>
 | 
				
			||||||
 | 
					            <div class="p-4 bg-white text-zinc-900">
 | 
				
			||||||
 | 
					                <p class="mb-4">{{ activeNotification.data.body }}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <p class="text-sm">{{ activeNotification.data.type }} | {{ activeNotification.data.created_at_date }} at {{ activeNotification.data.created_at_time }}</p>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </Modal>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										32
									
								
								src/resources/js/Components/Forms/CheckboxInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/resources/js/Components/Forms/CheckboxInput.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { computed } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// variables
 | 
				
			||||||
 | 
					const emit = defineEmits(['update:checked'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					  checked: {
 | 
				
			||||||
 | 
					    type: [Array, Boolean],
 | 
				
			||||||
 | 
					    default: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  value: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    default: null,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// computed properties
 | 
				
			||||||
 | 
					const proxyChecked = computed({
 | 
				
			||||||
 | 
					  get() {
 | 
				
			||||||
 | 
					    return props.checked
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set(val) {
 | 
				
			||||||
 | 
					    emit('update:checked', val)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <input type="checkbox" class="checkbox" :value="value" v-model="proxyChecked">
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										9
									
								
								src/resources/js/Components/Forms/InputError.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/resources/js/Components/Forms/InputError.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					defineProps({
 | 
				
			||||||
 | 
					  message: String,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <p v-show="message" class="px-2 py-1 text-sm text-red-600">{{ message }}</p>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/resources/js/Components/Forms/InputLabel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/resources/js/Components/Forms/InputLabel.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { useSlots, computed } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// defines
 | 
				
			||||||
 | 
					defineProps({
 | 
				
			||||||
 | 
					  value: String,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// computed properties
 | 
				
			||||||
 | 
					const hasActions = computed(() => !! useSlots().actions)
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <label class="label">
 | 
				
			||||||
 | 
					        <span class="label-text"><slot></slot></span>
 | 
				
			||||||
 | 
					        <span v-show="hasActions" class="flex items-center">
 | 
				
			||||||
 | 
					            <slot name="actions"></slot>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					    </label>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/resources/js/Components/Forms/RadioInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/resources/js/Components/Forms/RadioInput.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { computed } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// variables
 | 
				
			||||||
 | 
					const emit = defineEmits(['update:checked'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					  checked: {
 | 
				
			||||||
 | 
					    type: [Array, Boolean],
 | 
				
			||||||
 | 
					    default: false,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// computed properties
 | 
				
			||||||
 | 
					const proxyChecked = computed({
 | 
				
			||||||
 | 
					  get() {
 | 
				
			||||||
 | 
					    return props.checked
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set(val) {
 | 
				
			||||||
 | 
					    emit('update:checked', val)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <input type="radio" class="radio" v-model="proxyChecked">
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/resources/js/Components/Forms/TextInput.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/resources/js/Components/Forms/TextInput.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { ref, onMounted } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// defines
 | 
				
			||||||
 | 
					defineEmits(['update:modelValue'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineProps({
 | 
				
			||||||
 | 
					  modelValue: String,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineExpose({
 | 
				
			||||||
 | 
					  focus: () => input.value.focus()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// variables
 | 
				
			||||||
 | 
					const input = ref(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// lifecycle hooks
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  if (input.value.hasAttribute('autofocus')) {
 | 
				
			||||||
 | 
					    input.value.focus()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <input ref="input" class="input" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/resources/js/Components/Forms/Textarea.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/resources/js/Components/Forms/Textarea.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { ref, onMounted } from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// defines
 | 
				
			||||||
 | 
					defineEmits(['update:modelValue'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineProps({
 | 
				
			||||||
 | 
					  modelValue: String,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineExpose({
 | 
				
			||||||
 | 
					  focus: () => input.value.focus()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// variables
 | 
				
			||||||
 | 
					const input = ref(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// lifecycle hooks
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  if (input.value.hasAttribute('autofocus')) {
 | 
				
			||||||
 | 
					    input.value.focus()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <textarea ref="input" class="textarea" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
 | 
				
			||||||
 | 
					        <slot></slot>
 | 
				
			||||||
 | 
					    </textarea>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										52
									
								
								src/resources/js/Components/LanguageSwitcher.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/resources/js/Components/LanguageSwitcher.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					<script setup>
 | 
				
			||||||
 | 
					import { ref } from 'vue'
 | 
				
			||||||
 | 
					import { Inertia } from '@inertiajs/inertia'
 | 
				
			||||||
 | 
					import { usePage } from '@inertiajs/inertia-vue3'
 | 
				
			||||||
 | 
					import Dropdown from './DropdownMenu.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// variables
 | 
				
			||||||
 | 
					const currentLocale = ref(usePage().props.value.currentLocale)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// methods
 | 
				
			||||||
 | 
					const isCurrent = (language) => {
 | 
				
			||||||
 | 
					  return language.iso_code === currentLocale.value.iso_code
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const changeLocale = (language) => {
 | 
				
			||||||
 | 
					  if (language.iso_code !== currentLocale.value.iso_code) {
 | 
				
			||||||
 | 
					    Inertia.post(route('locale.set'), language, {
 | 
				
			||||||
 | 
					      replace: true,
 | 
				
			||||||
 | 
					      onSuccess: page => {
 | 
				
			||||||
 | 
					        currentLocale.value = page.props.currentLocale
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      onError: errors => {
 | 
				
			||||||
 | 
					console.log(errors)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					   <Dropdown align="right" width="60">
 | 
				
			||||||
 | 
					      <template #trigger>
 | 
				
			||||||
 | 
					          <div class="py-2 px-4 inline-grid grid-flow-col auto-cols-max gap-x-2 items-center bg-white hover:bg-gray-50 text-sm leading-4 font-medium rounded-md cursor-pointer">
 | 
				
			||||||
 | 
					              <svg class="h-4 w-8">
 | 
				
			||||||
 | 
					                  <use :href="`#flag-${currentLocale.iso_code}`"></use>
 | 
				
			||||||
 | 
					              </svg>
 | 
				
			||||||
 | 
					              <span class="ml-2">{{ currentLocale.localized_name }}</span>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <template #content>
 | 
				
			||||||
 | 
					          <div class="w-60">
 | 
				
			||||||
 | 
					              <button type="button" v-for="(lang, langIdx) in $page.props.availableLocales" :key="langIdx" @click="changeLocale(lang)" class="py-2 px-4 inline-grid grid-flow-col auto-cols-max gap-x-2 items-center w-full" :class="{ 'text-white bg-sky-600 cursor-default': isCurrent(lang), 'hover:bg-sky-600/50': !isCurrent(lang) }">
 | 
				
			||||||
 | 
					                  <svg class="h-4 w-8">
 | 
				
			||||||
 | 
					                      <use :href="`#flag-${lang.iso_code}`"></use>
 | 
				
			||||||
 | 
					                  </svg>
 | 
				
			||||||
 | 
					                  <span class="ml-2">{{ lang.localized_name }}</span>
 | 
				
			||||||
 | 
					              </button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					   </Dropdown>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
@@ -5,6 +5,7 @@ import { createInertiaApp } from '@inertiajs/inertia-vue3';
 | 
				
			|||||||
import { InertiaProgress } from '@inertiajs/progress';
 | 
					import { InertiaProgress } from '@inertiajs/progress';
 | 
				
			||||||
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
 | 
					import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
 | 
				
			||||||
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
 | 
					import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
 | 
				
			||||||
 | 
					import translationHelper from './base.js';
 | 
				
			||||||
import Notifications from 'notiwind';
 | 
					import Notifications from 'notiwind';
 | 
				
			||||||
import AppLayout from './Layouts/AppLayout.vue';
 | 
					import AppLayout from './Layouts/AppLayout.vue';
 | 
				
			||||||
import AuthLayout from './Layouts/AuthLayout.vue';
 | 
					import AuthLayout from './Layouts/AuthLayout.vue';
 | 
				
			||||||
@@ -43,6 +44,7 @@ createInertiaApp({
 | 
				
			|||||||
            .use(plugin)
 | 
					            .use(plugin)
 | 
				
			||||||
            .use(Notifications)
 | 
					            .use(Notifications)
 | 
				
			||||||
            .use(ZiggyVue, Ziggy)
 | 
					            .use(ZiggyVue, Ziggy)
 | 
				
			||||||
 | 
					            .mixin(translationHelper)
 | 
				
			||||||
            .mixin({ methods: { route } })
 | 
					            .mixin({ methods: { route } })
 | 
				
			||||||
            .mount(el);
 | 
					            .mount(el);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								src/resources/js/base.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/resources/js/base.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    __(key, replace = {}) {
 | 
				
			||||||
 | 
					      let translation = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // check for dot notation
 | 
				
			||||||
 | 
					      if (key.includes('.')) {
 | 
				
			||||||
 | 
					        translation = this.$page.props.language
 | 
				
			||||||
 | 
					        key.split('.').forEach(subKey => {
 | 
				
			||||||
 | 
					          translation = translation[subKey]
 | 
				
			||||||
 | 
					        }, key)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // check if it exists on the translated strings we got
 | 
				
			||||||
 | 
					        if (this.$page.props.language.hasOwnProperty(key)) {
 | 
				
			||||||
 | 
					          translation = this.$page.props.language[key]
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          // otherwise just take it raw
 | 
				
			||||||
 | 
					          translation = key
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // used to fill in variables for translation string
 | 
				
			||||||
 | 
					      Object.keys(replace).forEach(key => {
 | 
				
			||||||
 | 
					        translation = translation.replace(':' + key, replace[key])
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return translation;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/vite.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/vite.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					import fs from 'fs';
 | 
				
			||||||
 | 
					import { defineConfig, loadEnv } from 'vite';
 | 
				
			||||||
 | 
					import laravel from 'laravel-vite-plugin';
 | 
				
			||||||
 | 
					import vue from '@vitejs/plugin-vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ({ mode }) => {
 | 
				
			||||||
 | 
					    process.env = Object.assign(process.env, loadEnv(mode, process.cwd(), ''));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return defineConfig({
 | 
				
			||||||
 | 
					        build: {
 | 
				
			||||||
 | 
					            reportCompressedSize: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        plugins: [
 | 
				
			||||||
 | 
					            laravel({
 | 
				
			||||||
 | 
					                input: 'resources/js/app.js',
 | 
				
			||||||
 | 
					                ssr: 'resources/js/ssr.js',
 | 
				
			||||||
 | 
					                refresh: true,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            vue({
 | 
				
			||||||
 | 
					                template: {
 | 
				
			||||||
 | 
					                    transformAssetUrls: {
 | 
				
			||||||
 | 
					                        base: null,
 | 
				
			||||||
 | 
					                        includeAbsolute: false,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        ssr: {
 | 
				
			||||||
 | 
					            noExternal: ['@inertiajs/server'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        server: {
 | 
				
			||||||
 | 
					            host: process.env.APP_DOMAIN,
 | 
				
			||||||
 | 
					            https: {
 | 
				
			||||||
 | 
					                key: fs.readFileSync(`/code/docker/configs/nginx/ssls/${process.env.APP_DOMAIN}/${process.env.APP_DOMAIN}.key`),
 | 
				
			||||||
 | 
					                cert: fs.readFileSync(`/code/docker/configs/nginx/ssls/${process.env.APP_DOMAIN}/${process.env.APP_DOMAIN}.crt`),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user