refactor.

main
Giò 2024-07-01 20:55:25 +02:00
parent 944c993739
commit b7661d8e01
33 changed files with 213 additions and 655 deletions

View File

@ -8,52 +8,21 @@ LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug LOG_LEVEL=debug
DB_CONNECTION=mysql # mysql or mariadb connection
DB_HOST=127.0.0.1 # DB_CONNECTION=mysql
DB_PORT=3306 # DB_HOST=127.0.0.1
DB_DATABASE=laravel # DB_PORT=3306
DB_USERNAME=root # DB_DATABASE=laravel
DB_PASSWORD= # DB_USERNAME=root
# DB_PASSWORD=
# sqlite database
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
BROADCAST_DRIVER=log BROADCAST_DRIVER=log
CACHE_DRIVER=file CACHE_DRIVER=file
FILESYSTEM_DISK=local FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync QUEUE_CONNECTION=database
SESSION_DRIVER=file SESSION_DRIVER=file
SESSION_LIFETIME=120 SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1
VITE_APP_NAME="${APP_NAME}"
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

View File

@ -5,21 +5,21 @@ namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use App\Models\Regions; use App\Models\Regions;
class scrapeAddRegion extends Command class scraperAddRegion extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
* @var string * @var string
*/ */
protected $signature = 'scrape:add-region {name}'; protected $signature = 'scraper:add-region {name}';
/** /**
* The console command description. * The console command description.
* *
* @var string * @var string
*/ */
protected $description = 'Scrapes for properties from seeds.'; protected $description = 'Add new region to database.';
/** /**
* Execute the console command. * Execute the console command.

View File

@ -5,21 +5,21 @@ namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use App\Models\Seed; use App\Models\Seed;
class scrapeAddSeed extends Command class scraperAddSeed extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
* @var string * @var string
*/ */
protected $signature = 'scrape:add-seed {regionId} {uri}'; protected $signature = 'scraper:add-seed {regionId} {uri}';
/** /**
* The console command description. * The console command description.
* *
* @var string * @var string
*/ */
protected $description = 'Scrapes for properties from seeds.'; protected $description = 'Add new seed to database.';
/** /**
* Execute the console command. * Execute the console command.

View File

@ -6,14 +6,14 @@ use App\Scraper\Edomizil;
use App\Models\Property; use App\Models\Property;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class scrapeCreateJobs extends Command class scraperCreateJobs extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
* @var string * @var string
*/ */
protected $signature = 'scrape:jobs'; protected $signature = 'scraper:jobs';
/** /**
* The console command description. * The console command description.

View File

@ -5,21 +5,21 @@ namespace App\Console\Commands;
use App\Scraper\Edomizil; use App\Scraper\Edomizil;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class scrapeProperty extends Command class scraperProperty extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
* @var string * @var string
*/ */
protected $signature = 'scrape:property'; protected $signature = 'scraper:scrape-properties';
/** /**
* The console command description. * The console command description.
* *
* @var string * @var string
*/ */
protected $description = 'Scrapes for properties from seeds.'; protected $description = 'Scrape for properties.';
/** /**
* Execute the console command. * Execute the console command.

View File

@ -6,14 +6,14 @@ use App\Scraper\Edomizil;
use App\Models\Property; use App\Models\Property;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class scrapePropertyDate extends Command class scraperPropertyDate extends Command
{ {
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
* @var string * @var string
*/ */
protected $signature = 'scrape:propertydata {propertyId?} {--seed=}'; protected $signature = 'scraper:scrape-propertydata {propertyId?} {--seed=}';
/** /**
* The console command description. * The console command description.

View File

@ -7,6 +7,14 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel class Kernel extends ConsoleKernel
{ {
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/** /**
* Register the commands for the application. * Register the commands for the application.
*/ */

View File

@ -28,6 +28,6 @@ class ScrapePropertyData implements ShouldQueue
public function handle(): void public function handle(): void
{ {
Edomizil::scrapePropertyData($this->property); Edomizil::scrapePropertyData($this->property);
sleep(random_int(1,20)); sleep(random_int(1,10));
} }
} }

View File

@ -9,20 +9,9 @@ use App\Jobs\ScrapeProperty;
use App\Jobs\ScrapePropertyData; use App\Jobs\ScrapePropertyData;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
/**
* This Class contains methods for scraping offers from the
* website e-domizil.ch.
**/
class Edomizil{ class Edomizil{
/** public static function saveHttpException($response, $type, $entityId){
* Save an exception.
* @param string $response The respsonse form an exception e.g. 404
* @param enum $type Is either 'offer', 'price', 'calendar' or 'property'
* @param integer $entityId Has to be the id of the corresponding entity.
**/
public static function saveHttpException($response, $type, $entityId)
{
$exception = []; $exception = [];
@ -40,71 +29,52 @@ class Edomizil{
} }
/**
* Get seed urls.
* Get all seed urls (seeds.uris) in random order.
* @return Collection with seed urls.
**/
public static function getAllSeeds() public static function getAllSeeds()
{ {
// get all seeds from model in random order.
return Seed::select('id','uri')->inRandomOrder()->get(); return Seed::select('id','uri')->inRandomOrder()->get();
} }
/**
* Get property ids.
* Get all ids (properties.property_platform_id) in random order.
* @return Collection with property id
**/
public static function getAllProperties() public static function getAllProperties()
{ {
// get all properties from model in random order. // get all properties from model in random order.
return Property::select('id','property_platform_id')->inRandomOrder()->get(); return Property::select('id','property_platform_id')->inRandomOrder()->get();
} }
/** public static function dispatchPropertyJobs()
* Scrape for properties. {
* Scrapes for properties form seed url and save them to the database. $seeds = self::getAllSeeds();
* @param $seed Seed foreach($seeds as $seed){
**/ ScrapeProperty::dispatch($seed);
}
}
public static function dispatchPropertyDataJobs()
{
$properties = self::getAllProperties();
foreach($properties as $property){
ScrapePropertyData::dispatch($property);
}
}
public static function scrapeProperty($seed) public static function scrapeProperty($seed)
{ {
$response = Http::get($seed->uri); $response = Http::get($seed->uri);
if($response->successful()){ if($response->successful()){
$json = $response->json(); $json = $response->json();
/** Check if offers are findable in response */
if(!$json['offers']){
Exception::create([
'exception' => 'No offers found for'.$seed->uri,
'entity_type' => 'property',
'entity_id' => $property->id
]);
return;
}
/** Iterate offers */
foreach($json['offers'] as $offer){ foreach($json['offers'] as $offer){
/**
* Check if property with same id is already present in database.
* If already present check if the geoLocation was the same as the first time when found.
* Otherwise add property to database.
**/
$property = Property::firstWhere('property_platform_id', $offer['id']); $property = Property::firstWhere('property_platform_id', $offer['id']);
$geoLocation = implode(',', $offer['geoLocation']); $geoLocation = implode(',', $offer['geoLocation']);
if($property){ if($property){
/** Update last found attribute */
$property->last_found = now(); $property->last_found = now();
$property->save(); $property->save();
/** check if geoLocation is the same as at creation time and save exception if not */ // check if geoLocation is the same as last crawl
if($property->check_data !== $geoLocation){ if($property->check_data !== $geoLocation){
Exception::create([ Exception::create([
'exception' => 'geoLocation was different: '.$geoLocation, 'exception' => 'geoLocation was different: '.$geoLocation,
@ -112,7 +82,6 @@ class Edomizil{
'entity_id' => $property->id 'entity_id' => $property->id
]); ]);
} }
}else{ }else{
Property::create([ Property::create([
'property_platform_id' => $offer['id'], 'property_platform_id' => $offer['id'],
@ -127,32 +96,26 @@ class Edomizil{
}else{ }else{
/** Save Exception if document could not be found */
self::saveHttpException($response,'property', $seed->id); self::saveHttpException($response,'property', $seed->id);
return 0; return 0;
} }
} }
/**
* Extract details from property.
* Scrapes for offer, price and calendar details from property and save the to extractions table (or exceptions when not found).
* @param $property Id of property (properties.property_platform_id)
**/
public static function scrapePropertyData($property){ public static function scrapePropertyData($property){
$result = []; $result = [];
/** scrape offer details such as name, ammeneties, etc. */ // scrape offer details such as name etc.
$offer = Http::get('https://www.e-domizil.ch/rental/offer/'.$property->property_platform_id); $offer = Http::get('https://www.e-domizil.ch/rental/offer/'.$property->property_platform_id);
if($offer->successful()){ if($offer->successful()){
Extraction::create([ Extraction::create([
'property_id' => $property->id, 'property_id' => $property->id,
'type' => 'offer', 'type' => 'offer',
'body' => $offer->body(), 'body' => $offer->body(),
'header' => json_encode($offer->headers()) 'header' => json_encode($offer->headers())
]); ]);
}else{ }else{
self::saveHttpException($offer,'offer',$property->id); self::saveHttpException($offer,'offer',$property->id);
@ -160,7 +123,7 @@ class Edomizil{
$result['offer'] = $offer->body(); $result['offer'] = $offer->body();
/** scrape for price details */ // scrape price of property
$price = Http::get('https://www.e-domizil.ch/booking/checkout/priceDetails/'.$property->property_platform_id); $price = Http::get('https://www.e-domizil.ch/booking/checkout/priceDetails/'.$property->property_platform_id);
if($price->successful()){ if($price->successful()){
@ -178,7 +141,7 @@ class Edomizil{
$result['price'] = $price->body(); $result['price'] = $price->body();
/** scrape for calendar details */ // scrape calendar which contains occupancies
$calendar = Http::get('https://www.e-domizil.ch/api/v2/calendar/'.$property->property_platform_id, [ $calendar = Http::get('https://www.e-domizil.ch/api/v2/calendar/'.$property->property_platform_id, [
'year' => date("Y"), 'year' => date("Y"),
'month' => date("m") 'month' => date("m")
@ -203,28 +166,5 @@ class Edomizil{
} }
/** }
* Dispatch property jobs.
* Creates jobs for scraping new for properties
**/
public static function dispatchPropertyJobs()
{
$seeds = self::getAllSeeds();
foreach($seeds as $seed){
ScrapeProperty::dispatch($seed);
}
}
/**
* Dispatch property data jobs.
* Creates jobs for scraping new for property detail data.
**/
public static function dispatchPropertyDataJobs()
{
$properties = self::getAllProperties();
foreach($properties as $property){
ScrapePropertyData::dispatch($property);
}
}
}

View File

@ -1,4 +1,3 @@
{ {
"name": "laravel/laravel", "name": "laravel/laravel",
"type": "project", "type": "project",
@ -9,6 +8,7 @@
"php": "^8.1", "php": "^8.1",
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.10", "laravel/framework": "^10.10",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8" "laravel/tinker": "^2.8"
}, },
"require-dev": { "require-dev": {
@ -35,8 +35,7 @@
"scripts": { "scripts": {
"post-autoload-dump": [ "post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi", "@php artisan package:discover --ansi"
"@php artisan filament:upgrade"
], ],
"post-update-cmd": [ "post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force" "@php artisan vendor:publish --tag=laravel-assets --ansi --force"

68
scraper/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "2db4f4f45d5f19c3d1d1d0592de24b8a", "content-hash": "9c491b8531eec05ba41a11d9276a5749",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -1315,6 +1315,72 @@
}, },
"time": "2024-06-17T13:58:22+00:00" "time": "2024-06-17T13:58:22+00:00"
}, },
{
"name": "laravel/sanctum",
"version": "v3.3.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/sanctum.git",
"reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/sanctum/zipball/8c104366459739f3ada0e994bcd3e6fd681ce3d5",
"reference": "8c104366459739f3ada0e994bcd3e6fd681ce3d5",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/console": "^9.21|^10.0",
"illuminate/contracts": "^9.21|^10.0",
"illuminate/database": "^9.21|^10.0",
"illuminate/support": "^9.21|^10.0",
"php": "^8.0.2"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.28.2|^8.8.3",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Sanctum\\SanctumServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Sanctum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
"keywords": [
"auth",
"laravel",
"sanctum"
],
"support": {
"issues": "https://github.com/laravel/sanctum/issues",
"source": "https://github.com/laravel/sanctum"
},
"time": "2023-12-19T18:44:48+00:00"
},
{ {
"name": "laravel/serializable-closure", "name": "laravel/serializable-closure",
"version": "v1.3.3", "version": "v1.3.3",

View File

@ -165,6 +165,7 @@ return [
*/ */
App\Providers\AppServiceProvider::class, App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class, App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class, App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class, App\Providers\RouteServiceProvider::class,
])->toArray(), ])->toArray(),

View File

@ -0,0 +1,83 @@
<?php
use Laravel\Sanctum\Sanctum;
return [
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
Sanctum::currentApplicationUrlWithPort()
))),
/*
|--------------------------------------------------------------------------
| Sanctum Guards
|--------------------------------------------------------------------------
|
| This array contains the authentication guards that will be checked when
| Sanctum is trying to authenticate a request. If none of these guards
| are able to authenticate the request, Sanctum will use the bearer
| token that's present on an incoming request for authentication.
|
*/
'guard' => ['web'],
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. This will override any values set in the token's
| "expires_at" attribute, but first-party sessions are not affected.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Token Prefix
|--------------------------------------------------------------------------
|
| Sanctum can prefix new tokens in order to take advantage of numerous
| security scanning initiatives maintained by open source platforms
| that notify developers if they commit tokens into repositories.
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
*/
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
],
];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
function r({state:i}){return{state:i,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=o=>o===null?0:Array.isArray(o)?o.length:typeof o!="object"?0:Object.keys(o).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows),s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.rows=e,this.updateState()},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{["x-on:blur"]:"createTag()",["x-model"]:"newTag",["x-on:keydown"](t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},["x-on:paste"](){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default};

View File

@ -1 +0,0 @@
function t({initialHeight:e}){return{init:function(){this.render()},render:function(){this.$el.scrollHeight>0&&(this.$el.style.height=e+"rem",this.$el.style.height=this.$el.scrollHeight+"px")}}}export{t as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
function c(){return{collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,init:function(){this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1})},mountAction:function(e,s=null){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableAction(e,s)},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){if(this.isLoading=!0,this.areRecordsSelected(this.getRecordsInGroupOnPage(e))){this.deselectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e));return}this.selectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e)),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let s=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])t.dataset.group===e&&s.push(t.value);return s},getRecordsOnPage:function(){let e=[];for(let s of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(s.value);return e},selectRecords:function(e){for(let s of e)this.isRecordSelected(s)||this.selectedRecords.push(s)},deselectRecords:function(e){for(let s of e){let t=this.selectedRecords.indexOf(s);t!==-1&&this.selectedRecords.splice(t,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(s=>this.isRecordSelected(s))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]}}}export{c as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long