Compare commits
5 Commits
63216ae7e8
...
jobs/hellc
Author | SHA1 | Date | |
---|---|---|---|
e8b9517664 | |||
cfbae6ddbf | |||
4368aae6c4 | |||
070235e011 | |||
ad10dcaa0f |
@ -25,12 +25,12 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
|||||||
private const STEAM_LOGIN_THRESHOLD = 5 * 60; // 5 minutes
|
private const STEAM_LOGIN_THRESHOLD = 5 * 60; // 5 minutes
|
||||||
private const APPROXIMATIVE_RUNNING_MINUTES = 2;
|
private const APPROXIMATIVE_RUNNING_MINUTES = 2;
|
||||||
|
|
||||||
private JobRun $jobRun;
|
protected JobRun $jobRun;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct($jobId = 2)
|
||||||
{
|
{
|
||||||
Log::info("Constructing HellcaseJob");
|
Log::info("Constructing HellcaseJob");
|
||||||
parent::__construct(2);
|
parent::__construct($jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(Browser $browser): ?JobRun
|
public function run(Browser $browser): ?JobRun
|
||||||
@ -52,7 +52,7 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->jobRun->success = false;
|
$this->jobRun->success = false;
|
||||||
$this->jobRun->save();
|
$this->jobRun->save();
|
||||||
AllNotification::send(new JobErrorNotification($this->jobId, "Erreur lors de la participation aux concours gratuits", $e->getMessage()));
|
AllNotification::send(new JobErrorNotification($this->jobId, "Erreur lors de la participation aux concours gratuits : " . $e->getMessage()));
|
||||||
}
|
}
|
||||||
$this->getDailyFree($browser);
|
$this->getDailyFree($browser);
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function signin(Browser $browser)
|
protected function signin(Browser $browser)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$browser->clickAtXPath('//button[.//span[text() = "Sign in"]]');
|
$browser->clickAtXPath('//button[.//span[text() = "Sign in"]]');
|
||||||
@ -170,16 +170,17 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
|||||||
"content" => ""
|
"content" => ""
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
$nextSlideButton = $browser->driver->findElement(WebDriverBy::xpath('//button[@class="_button_1ygbm_7 _next_1ygbm_24"]'));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
|
||||||
|
AllNotification::send(new JobDebugNotification($this->jobId, "No next slide button found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach ($buttons as $button) {
|
foreach ($buttons as $button) {
|
||||||
try {
|
// Click the next slide button if the button is not clickable
|
||||||
$nextSlideButton = $browser->driver->findElement(WebDriverBy::xpath('//button[@class="_button_1ygbm_7 _next_1ygbm_24"]'));
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
|
|
||||||
AllNotification::send(new JobDebugNotification($this->jobId, "No next slide button found"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$clickedFailsCounter = 0;
|
$clickedFailsCounter = 0;
|
||||||
while ($clickedFailsCounter < 7 && $clickedFailsCounter > 0) {
|
while ($clickedFailsCounter < 7 && $clickedFailsCounter >= 0) {
|
||||||
try {
|
try {
|
||||||
$button->click();
|
$button->click();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@ -192,7 +193,8 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
|||||||
}
|
}
|
||||||
$clickedFailsCounter = -1;
|
$clickedFailsCounter = -1;
|
||||||
}
|
}
|
||||||
sleep(5);
|
|
||||||
|
sleep(5); // Wait a bit for loading
|
||||||
$this->joinGiveaway($browser);
|
$this->joinGiveaway($browser);
|
||||||
$browser->within(new MainNav, function (Browser $browser) {
|
$browser->within(new MainNav, function (Browser $browser) {
|
||||||
$browser->goToHome();
|
$browser->goToHome();
|
||||||
@ -388,7 +390,7 @@ class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
|||||||
$browser->clickAtXPath('//*[contains(text(), "Edit Profile")]');
|
$browser->clickAtXPath('//*[contains(text(), "Edit Profile")]');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function removePopups(Browser $browser)
|
protected function removePopups(Browser $browser)
|
||||||
{
|
{
|
||||||
// $browser->script('document.querySelector("div.app-modal")[0].remove();');
|
// $browser->script('document.querySelector("div.app-modal")[0].remove();');
|
||||||
// $browser->driver->executeScript('document.querySelector("div.app-modal")[0].remove();');
|
// $browser->driver->executeScript('document.querySelector("div.app-modal")[0].remove();');
|
||||||
|
145
app/Browser/Jobs/HellcaseBattles/HellcaseBattlesJob.php
Normal file
145
app/Browser/Jobs/HellcaseBattles/HellcaseBattlesJob.php
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Browser\Jobs\HellcaseBattles;
|
||||||
|
|
||||||
|
use App\Browser\Jobs\Hellcase\HellcaseJob;
|
||||||
|
use App\Models\HellcaseBattle;
|
||||||
|
use App\Models\Job;
|
||||||
|
use App\Models\JobRun;
|
||||||
|
use App\Notification\Notifications\JobDebugNotification;
|
||||||
|
use App\Notification\Providers\AllNotification;
|
||||||
|
use Exception;
|
||||||
|
use Facebook\WebDriver\WebDriver;
|
||||||
|
use Facebook\WebDriver\WebDriverBy;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Laravel\Dusk\Browser;
|
||||||
|
|
||||||
|
class HellcaseBattlesJob extends HellcaseJob implements ShouldBeUniqueUntilProcessing
|
||||||
|
{
|
||||||
|
private Collection $jobInfos;
|
||||||
|
private array $battlesToAdd = [];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
Log::info("Constructing HellcaseBattlesJob");
|
||||||
|
parent::__construct(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(Browser $browser): ?JobRun
|
||||||
|
{
|
||||||
|
$this->jobInfos = Job::find($this->jobId)->jobInfosTable();
|
||||||
|
Log::info("Running HellcaseBattlesJob");
|
||||||
|
$this->jobRun = new JobRun([
|
||||||
|
"job_id" => $this->jobId,
|
||||||
|
"success" => false,
|
||||||
|
]);
|
||||||
|
$this->jobRun->save();
|
||||||
|
|
||||||
|
$browser->visit('https://hellcase.com');
|
||||||
|
$browser->waitForText("CASES", 30, true);
|
||||||
|
$this->removePopups($browser);
|
||||||
|
sleep(5);
|
||||||
|
$this->signin($browser);
|
||||||
|
|
||||||
|
$this->saveInterestingBattles($browser);
|
||||||
|
|
||||||
|
$this->sendFinishedBattles($browser);
|
||||||
|
|
||||||
|
$this->createNewBattles();
|
||||||
|
|
||||||
|
$this->jobRun->success = true;
|
||||||
|
$this->jobRun->save();
|
||||||
|
|
||||||
|
Log::info("HellcaseBattlesJob run ended");
|
||||||
|
|
||||||
|
return $this->jobRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save current cases battles to database for later processing
|
||||||
|
* @param \Laravel\Dusk\Browser $browser
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function saveInterestingBattles(Browser $browser)
|
||||||
|
{
|
||||||
|
$battleIndex = 0; // Index of the battle to get info from
|
||||||
|
$running = true;
|
||||||
|
while ($running) {
|
||||||
|
$browser->visit('https://hellcase.com/casebattle');
|
||||||
|
$browser->waitForText("CASES", 30, true);
|
||||||
|
|
||||||
|
AllNotification::send(new JobDebugNotification($this->jobId, "I hate niggers"));
|
||||||
|
|
||||||
|
// Sort by price
|
||||||
|
try {
|
||||||
|
$sortByPriceDiv = $browser->driver->findElement(WebDriverBy::xpath("//*[span[contains(text(), 'Value')]]"));
|
||||||
|
$sortByPriceDiv->click();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
AllNotification::send(new JobDebugNotification($this->jobId, "Failed to sort by price"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(5);
|
||||||
|
|
||||||
|
$battles = $browser->driver->findElements(WebDriverBy::xpath("//*[contains(@class, 'casebattle-table__item')]"));
|
||||||
|
$battle = $battles[$battleIndex];
|
||||||
|
$battleIndex++;
|
||||||
|
$browser->scrollIntoView(".casebattle-table__item:nth-child(" . max($battleIndex -1, 1) . ")");
|
||||||
|
sleep(2);
|
||||||
|
$battleValue = floatval(
|
||||||
|
explode(
|
||||||
|
"\n",
|
||||||
|
$battle->findElement(WebDriverBy::xpath("./div/div[contains(@class, 'core-price')]"))->getDomProperty("innerText")
|
||||||
|
)[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($battleValue < floatval($this->jobInfos->get("hellcase_battles_minimum_value"))) {
|
||||||
|
$running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$battleLinkButton = $battle->findElement(WebDriverBy::xpath('./div//button[text() = "watch"]'));
|
||||||
|
$battleLinkButton->sendKeys("\n");
|
||||||
|
sleep(3);
|
||||||
|
$battleLink = $browser->driver->getCurrentURL();
|
||||||
|
|
||||||
|
$this->battlesToAdd[$battleLink] = $battleValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendFinishedBattles(Browser $browser) {
|
||||||
|
// foreach battle that we didn"t already planned to add with $this->battlesToAdd
|
||||||
|
foreach (HellcaseBattle::all() as $battle) {
|
||||||
|
dump($battle);
|
||||||
|
if (!array_key_exists($battle->getUrl(), $this->battlesToAdd)) {
|
||||||
|
dump("finished");
|
||||||
|
$browser->visit($battle->getUrl());
|
||||||
|
|
||||||
|
try {
|
||||||
|
$browser->waitForText("Started at");
|
||||||
|
// Send the battle
|
||||||
|
$this->sendBattle($browser, $battle);
|
||||||
|
} catch (Exception $e) { // Battle is not finished or error (like battle cancelled)
|
||||||
|
}
|
||||||
|
|
||||||
|
$battle->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendBattle(Browser $browser, HellcaseBattle $battle) {
|
||||||
|
AllNotification::send(new JobDebugNotification($this->jobId, "Battle sent" . $battle->getUrl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createNewBattles() {
|
||||||
|
foreach ($this->battlesToAdd as $battleLink => $battleValue) {
|
||||||
|
$battleLink = explode("/", $battleLink);
|
||||||
|
HellcaseBattle::firstOrCreate([
|
||||||
|
"battle_id" => $battleLink[count($battleLink) - 1],
|
||||||
|
"value" => $battleValue,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ class JobController extends Controller
|
|||||||
public function show($jobId, Request $request)
|
public function show($jobId, Request $request)
|
||||||
{
|
{
|
||||||
return Inertia::render('Job', [
|
return Inertia::render('Job', [
|
||||||
'job' => Job::where('id', $jobId)->with('jobInfos')->first(),
|
'job' => Job::where('id', $jobId)->with('jobInfos', 'jobRuns')->first(),
|
||||||
'error' => $request->input('error'),
|
'error' => $request->input('error'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
17
app/Models/HellcaseBattle.php
Normal file
17
app/Models/HellcaseBattle.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class HellcaseBattle extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
"battle_id",
|
||||||
|
"value",
|
||||||
|
];
|
||||||
|
|
||||||
|
public function getUrl() {
|
||||||
|
return "https://hellcase.com/casebattle/{$this->battle_id}";
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,16 @@ class Job extends Model
|
|||||||
return $this->hasMany(JobInfo::class)->with("jobInfoType")->orderBy("created_at");
|
return $this->hasMany(JobInfo::class)->with("jobInfoType")->orderBy("created_at");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an associative collection of the job infos with their values
|
||||||
|
* @return \Illuminate\Database\Eloquent\Collection<string, string>>
|
||||||
|
*/
|
||||||
|
public function jobInfosTable() {
|
||||||
|
return $this->jobInfos->mapWithKeys(function ($jobInfo) {
|
||||||
|
return [$jobInfo->key => $jobInfo->value];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function jobRuns()
|
public function jobRuns()
|
||||||
{
|
{
|
||||||
return $this->hasMany(JobRun::class)->orderBy("created_at");
|
return $this->hasMany(JobRun::class)->orderBy("created_at");
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
"$schema": "https://shadcn-vue.com/schema.json",
|
"$schema": "https://shadcn-vue.com/schema.json",
|
||||||
"style": "default",
|
"style": "default",
|
||||||
"typescript": true,
|
"typescript": true,
|
||||||
"tsConfigPath": "./tsconfig.json",
|
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.js",
|
"config": "tailwind.config.js",
|
||||||
"css": "resources/css/app.css",
|
"css": "resources/css/app.css",
|
||||||
@ -10,9 +9,12 @@
|
|||||||
"cssVariables": true,
|
"cssVariables": true,
|
||||||
"prefix": ""
|
"prefix": ""
|
||||||
},
|
},
|
||||||
"framework": "laravel",
|
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "@/Components",
|
"components": "@/Components",
|
||||||
"utils": "@/lib/utils"
|
"composables": "@/composables",
|
||||||
}
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/Components/ui",
|
||||||
|
"lib": "@/lib"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Job;
|
||||||
|
use App\Models\JobInfo;
|
||||||
|
use App\Models\JobInfoType;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$newJobId = 3;
|
||||||
|
Job::forceCreate([
|
||||||
|
"id" => $newJobId,
|
||||||
|
"name" => "Hellcase Battles",
|
||||||
|
"description" => "Envoie les meilleures battles d'Hellcase",
|
||||||
|
]);
|
||||||
|
|
||||||
|
JobInfo::forceCreate([
|
||||||
|
"key" => "hellcase_battles_discord_webhook_url",
|
||||||
|
"name" => "Webhook Discord",
|
||||||
|
"description" => "Le lien discord webhook utilisé pour envoyer les meilleures battles d'Hellcase.\nSi aucun n'est spécifié, le webhook Discord des paramètres généraux sera utilisé.",
|
||||||
|
"placeholder" => "https://discord.com/api/webhooks/...",
|
||||||
|
"is_required" => false,
|
||||||
|
"job_info_type_id" => 4,
|
||||||
|
"job_id" => $newJobId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
JobInfoType::forceCreate([
|
||||||
|
"id" => 5,
|
||||||
|
"name" => "number",
|
||||||
|
]);
|
||||||
|
JobInfoType::forceCreate([
|
||||||
|
"id" => 6,
|
||||||
|
"name" => "boolean",
|
||||||
|
]);
|
||||||
|
|
||||||
|
JobInfo::forceCreate([
|
||||||
|
"key" => "hellcase_battles_minimum_value",
|
||||||
|
"name" => "Valeur minimum des battles",
|
||||||
|
"description" => "La valeur minimale qu'une battle doit avoir pour être envoyée, en euros.",
|
||||||
|
"placeholder" => "1000",
|
||||||
|
"job_info_type_id" => 5,
|
||||||
|
"job_id" => $newJobId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
JobInfo::forceCreate([
|
||||||
|
"key" => "hellcase_battles_allow_bots",
|
||||||
|
"name" => "Autoriser les battles avec bots",
|
||||||
|
"description" => "Envoyer les battles avec un seul joueur et des bots.",
|
||||||
|
"is_required" => false,
|
||||||
|
"job_info_type_id" => 6,
|
||||||
|
"job_id" => $newJobId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Schema::create('hellcase_battles', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->string("battle_id")->unique();
|
||||||
|
$table->float("value");
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Job::where("id", 3)->delete();
|
||||||
|
JobInfo::where("job_id", 3)->delete();
|
||||||
|
JobInfoType::whereIn("id", [5, 6])->delete();
|
||||||
|
|
||||||
|
Schema::dropIfExists('hellcase_battles');
|
||||||
|
}
|
||||||
|
};
|
77
package-lock.json
generated
77
package-lock.json
generated
@ -11,6 +11,7 @@
|
|||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"lucide-vue-next": "^0.474.0",
|
"lucide-vue-next": "^0.474.0",
|
||||||
"radix-vue": "^1.9.13",
|
"radix-vue": "^1.9.13",
|
||||||
|
"reka-ui": "^2.1.0",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
@ -21,8 +22,10 @@
|
|||||||
"autoprefixer": "^10.4.12",
|
"autoprefixer": "^10.4.12",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
|
"laravel-echo": "^2.0.2",
|
||||||
"laravel-vite-plugin": "^1.2.0",
|
"laravel-vite-plugin": "^1.2.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
|
"pusher-js": "^8.4.0",
|
||||||
"sass-embedded": "^1.83.4",
|
"sass-embedded": "^1.83.4",
|
||||||
"tailwindcss": "^3.2.1",
|
"tailwindcss": "^3.2.1",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
@ -956,20 +959,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/virtual-core": {
|
"node_modules/@tanstack/virtual-core": {
|
||||||
"version": "3.11.3",
|
"version": "3.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.4.tgz",
|
||||||
"integrity": "sha512-v2mrNSnMwnPJtcVqNvV0c5roGCBqeogN8jDtgtuHCphdwBasOZ17x8UV8qpHUh+u0MLfX43c0uUHKje0s+Zb0w==",
|
"integrity": "sha512-fNGO9fjjSLns87tlcto106enQQLycCKR4DPNpgq3djP5IdcPFdPAmaKjsgzIeRhH7hWrELgW12hYnRthS5kLUw==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/vue-virtual": {
|
"node_modules/@tanstack/vue-virtual": {
|
||||||
"version": "3.11.3",
|
"version": "3.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.4.tgz",
|
||||||
"integrity": "sha512-BVZ00i5XBucetRj2doVd32jOPtJthvZSVJvx9GL4gSQsyngliSCtzlP1Op7TFrEtmebRKT8QUQE1tRhOQzWecQ==",
|
"integrity": "sha512-1fPrd3hE1SS4R/9JbX1AlzueY4duCK7ixuLcMW5GMnk9N6WbLo9MioNKiv22V+UaXKOLNy8tLdzT8NYerOFTOQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/virtual-core": "3.11.3"
|
"@tanstack/virtual-core": "3.13.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -2239,6 +2242,15 @@
|
|||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/laravel-echo": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ciai6hA7r35MFqNRb8G034cvm9WiveSTFQQKRGJhWtZGbng7C8BBa5QvqDxk/Mw5GeJ+q19jrEwQhf7r1b1lcg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/laravel-vite-plugin": {
|
"node_modules/laravel-vite-plugin": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.2.0.tgz",
|
||||||
@ -2487,6 +2499,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ohash": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="
|
||||||
|
},
|
||||||
"node_modules/package-json-from-dist": {
|
"node_modules/package-json-from-dist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||||
@ -2700,6 +2717,15 @@
|
|||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/pusher-js": {
|
||||||
|
"version": "8.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.4.0.tgz",
|
||||||
|
"integrity": "sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tweetnacl": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||||
@ -2847,6 +2873,37 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reka-ui": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-w4kEDEyXhIqv4QeFJeiuBc4mQP37hH/UTRpEb9dMbPdR49JG5TcV/s0+ntNRONUUW4LDLX7E1ZPcwBw5hnu0yw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.6.13",
|
||||||
|
"@floating-ui/vue": "^1.1.6",
|
||||||
|
"@internationalized/date": "^3.5.0",
|
||||||
|
"@internationalized/number": "^3.5.0",
|
||||||
|
"@tanstack/vue-virtual": "^3.12.0",
|
||||||
|
"@vueuse/core": "^12.5.0",
|
||||||
|
"@vueuse/shared": "^12.5.0",
|
||||||
|
"aria-hidden": "^1.2.4",
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"ohash": "^1.1.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">= 3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/reka-ui/node_modules/@vueuse/shared": {
|
||||||
|
"version": "12.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz",
|
||||||
|
"integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.5.13"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
@ -3697,6 +3754,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/tweetnacl": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.7.3",
|
"version": "5.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||||
|
@ -24,12 +24,14 @@
|
|||||||
"vue-tsc": "^2.0.24"
|
"vue-tsc": "^2.0.24"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tanstack/vue-table": "^8.21.2",
|
||||||
"@vueuse/core": "^12.5.0",
|
"@vueuse/core": "^12.5.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"lucide-vue-next": "^0.474.0",
|
"lucide-vue-next": "^0.474.0",
|
||||||
"radix-vue": "^1.9.13",
|
"radix-vue": "^1.9.13",
|
||||||
|
"reka-ui": "^2.1.0",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
}
|
}
|
||||||
|
50
pnpm-lock.yaml
generated
50
pnpm-lock.yaml
generated
@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@tanstack/vue-table':
|
||||||
|
specifier: ^8.21.2
|
||||||
|
version: 8.21.2(vue@3.5.13(typescript@5.7.3))
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^12.5.0
|
specifier: ^12.5.0
|
||||||
version: 12.7.0(typescript@5.7.3)
|
version: 12.7.0(typescript@5.7.3)
|
||||||
@ -26,6 +29,9 @@ importers:
|
|||||||
radix-vue:
|
radix-vue:
|
||||||
specifier: ^1.9.13
|
specifier: ^1.9.13
|
||||||
version: 1.9.16(vue@3.5.13(typescript@5.7.3))
|
version: 1.9.16(vue@3.5.13(typescript@5.7.3))
|
||||||
|
reka-ui:
|
||||||
|
specifier: ^2.1.0
|
||||||
|
version: 2.1.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^2.6.0
|
specifier: ^2.6.0
|
||||||
version: 2.6.0
|
version: 2.6.0
|
||||||
@ -425,9 +431,19 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
|
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
|
||||||
|
|
||||||
|
'@tanstack/table-core@8.21.2':
|
||||||
|
resolution: {integrity: sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
'@tanstack/virtual-core@3.13.2':
|
'@tanstack/virtual-core@3.13.2':
|
||||||
resolution: {integrity: sha512-Qzz4EgzMbO5gKrmqUondCjiHcuu4B1ftHb0pjCut661lXZdGoHeze9f/M8iwsK1t5LGR6aNuNGU7mxkowaW6RQ==}
|
resolution: {integrity: sha512-Qzz4EgzMbO5gKrmqUondCjiHcuu4B1ftHb0pjCut661lXZdGoHeze9f/M8iwsK1t5LGR6aNuNGU7mxkowaW6RQ==}
|
||||||
|
|
||||||
|
'@tanstack/vue-table@8.21.2':
|
||||||
|
resolution: {integrity: sha512-KBgOWxha/x4m1EdhVWxOpqHb661UjqAxzPcmXR3QiA7aShZ547x19Gw0UJX9we+m+tVcPuLRZ61JsYW47QZFfQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
peerDependencies:
|
||||||
|
vue: '>=3.2'
|
||||||
|
|
||||||
'@tanstack/vue-virtual@3.13.2':
|
'@tanstack/vue-virtual@3.13.2':
|
||||||
resolution: {integrity: sha512-z4swzjdhzCh95n9dw9lTvw+t3iwSkYRlVkYkra3C9mul/m5fTzHR7KmtkwH4qXMTXGJUbngtC/bz2cHQIHkO8g==}
|
resolution: {integrity: sha512-z4swzjdhzCh95n9dw9lTvw+t3iwSkYRlVkYkra3C9mul/m5fTzHR7KmtkwH4qXMTXGJUbngtC/bz2cHQIHkO8g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -952,6 +968,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
|
resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
ohash@1.1.6:
|
||||||
|
resolution: {integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==}
|
||||||
|
|
||||||
package-json-from-dist@1.0.1:
|
package-json-from-dist@1.0.1:
|
||||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||||
|
|
||||||
@ -1054,6 +1073,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
engines: {node: '>=8.10.0'}
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
||||||
|
reka-ui@2.1.0:
|
||||||
|
resolution: {integrity: sha512-w4kEDEyXhIqv4QeFJeiuBc4mQP37hH/UTRpEb9dMbPdR49JG5TcV/s0+ntNRONUUW4LDLX7E1ZPcwBw5hnu0yw==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: '>= 3.2.0'
|
||||||
|
|
||||||
require-directory@2.1.1:
|
require-directory@2.1.1:
|
||||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -1681,8 +1705,15 @@ snapshots:
|
|||||||
mini-svg-data-uri: 1.4.4
|
mini-svg-data-uri: 1.4.4
|
||||||
tailwindcss: 3.4.17
|
tailwindcss: 3.4.17
|
||||||
|
|
||||||
|
'@tanstack/table-core@8.21.2': {}
|
||||||
|
|
||||||
'@tanstack/virtual-core@3.13.2': {}
|
'@tanstack/virtual-core@3.13.2': {}
|
||||||
|
|
||||||
|
'@tanstack/vue-table@8.21.2(vue@3.5.13(typescript@5.7.3))':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/table-core': 8.21.2
|
||||||
|
vue: 3.5.13(typescript@5.7.3)
|
||||||
|
|
||||||
'@tanstack/vue-virtual@3.13.2(vue@3.5.13(typescript@5.7.3))':
|
'@tanstack/vue-virtual@3.13.2(vue@3.5.13(typescript@5.7.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/virtual-core': 3.13.2
|
'@tanstack/virtual-core': 3.13.2
|
||||||
@ -2224,6 +2255,8 @@ snapshots:
|
|||||||
|
|
||||||
object-inspect@1.13.3: {}
|
object-inspect@1.13.3: {}
|
||||||
|
|
||||||
|
ohash@1.1.6: {}
|
||||||
|
|
||||||
package-json-from-dist@1.0.1: {}
|
package-json-from-dist@1.0.1: {}
|
||||||
|
|
||||||
path-browserify@1.0.1: {}
|
path-browserify@1.0.1: {}
|
||||||
@ -2321,6 +2354,23 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
reka-ui@2.1.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)):
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/dom': 1.6.13
|
||||||
|
'@floating-ui/vue': 1.1.6(vue@3.5.13(typescript@5.7.3))
|
||||||
|
'@internationalized/date': 3.7.0
|
||||||
|
'@internationalized/number': 3.6.0
|
||||||
|
'@tanstack/vue-virtual': 3.13.2(vue@3.5.13(typescript@5.7.3))
|
||||||
|
'@vueuse/core': 12.7.0(typescript@5.7.3)
|
||||||
|
'@vueuse/shared': 12.7.0(typescript@5.7.3)
|
||||||
|
aria-hidden: 1.2.4
|
||||||
|
defu: 6.1.4
|
||||||
|
ohash: 1.1.6
|
||||||
|
vue: 3.5.13(typescript@5.7.3)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@vue/composition-api'
|
||||||
|
- typescript
|
||||||
|
|
||||||
require-directory@2.1.1: {}
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
resolve@1.22.10:
|
resolve@1.22.10:
|
||||||
|
@ -18,7 +18,7 @@ const jobInfoType = props.jobInfo.job_info_type.name;
|
|||||||
<div>
|
<div>
|
||||||
<Label :for="'' + jobInfo.id" class="text">{{ jobInfo.name }}<span v-if="jobInfo.is_required" class="cursor-help" title="Requis" aria-label="Requis">*</span></Label>
|
<Label :for="'' + jobInfo.id" class="text">{{ jobInfo.name }}<span v-if="jobInfo.is_required" class="cursor-help" title="Requis" aria-label="Requis">*</span></Label>
|
||||||
<Description>{{ jobInfo.description }}</Description>
|
<Description>{{ jobInfo.description }}</Description>
|
||||||
<Input v-if="jobInfoType != 'checkbox'" :type="jobInfoType" :id="'' + jobInfo.id" :name="'' + jobInfo.id" :placeholder="jobInfo.placeholder" v-model="jobInfo.value as string" :required="jobInfo.is_required" />
|
<Input v-if="['text', 'email', 'password', 'url', 'number'].includes(jobInfoType)" :type="jobInfoType" :id="'' + jobInfo.id" :name="'' + jobInfo.id" :placeholder="jobInfo.placeholder" v-model="jobInfo.value as string" :required="jobInfo.is_required" />
|
||||||
<VModelCheckbox v-else :id="'' + jobInfo.id" :class="''" v-model="jobInfo.value as boolean" />
|
<VModelCheckbox v-else :id="'' + jobInfo.id" :class="''" v-model="jobInfo.value as boolean" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Separator from "@/Components/ui/separator/Separator.vue";
|
||||||
|
import { JobRunArtifact } from "@/types/Jobs/job";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
jobRun: JobRunArtifact;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul>
|
||||||
|
<li v-for="artifact in jobRun.artifacts" :key="artifact.id">
|
||||||
|
<p>{{ artifact.name }}</p>
|
||||||
|
<p class="italic">{{ artifact.content }}</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
ul {
|
||||||
|
list-style-type: circle;
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
28
resources/js/Components/Layout/Job/JobRuns/JobRunItem.vue
Normal file
28
resources/js/Components/Layout/Job/JobRuns/JobRunItem.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import AccordionContent from "@/Components/ui/accordion/AccordionContent.vue";
|
||||||
|
import AccordionItem from "@/Components/ui/accordion/AccordionItem.vue";
|
||||||
|
import AccordionTrigger from "@/Components/ui/accordion/AccordionTrigger.vue";
|
||||||
|
import { JobRunArtifact } from "@/types/Jobs/job";
|
||||||
|
import JobRunArtifacts from "./JobRunArtifacts.vue";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
jobRun: JobRunArtifact;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionItem :value="''+jobRun.id" :class="[jobRun.success ? 'bg-green-100' : 'bg-red-200', 'first:rounded-t last:rounded-b', 'px-3']">
|
||||||
|
<AccordionTrigger>
|
||||||
|
{{ new Date(Date.parse(jobRun.created_at)).toLocaleTimeString(undefined, {
|
||||||
|
weekday: "long",
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
<JobRunArtifacts :jobRun="jobRun" />
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</template>
|
30
resources/js/Components/Layout/Job/JobRuns/JobRuns.vue
Normal file
30
resources/js/Components/Layout/Job/JobRuns/JobRuns.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Job, JobRunArtifact } from "@/types/Jobs/job";
|
||||||
|
import JobRunItem from "./JobRunItem.vue";
|
||||||
|
import Accordion from "@/Components/ui/accordion/Accordion.vue";
|
||||||
|
import ScrollArea from "@/Components/ui/scroll-area/ScrollArea.vue";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
job: Job;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="job.job_runs.length > 0">
|
||||||
|
<h2>Ancien jobs</h2>
|
||||||
|
<ScrollArea class="min-h-[300px] max-h-[20vh] overflow-auto pr-2">
|
||||||
|
<Accordion type="multiple" collapsible>
|
||||||
|
<JobRunItem
|
||||||
|
:jobRun="jobRun"
|
||||||
|
v-for="jobRun in job.job_runs.sort((a, b) => {
|
||||||
|
return (
|
||||||
|
new Date(b.created_at).getTime() -
|
||||||
|
new Date(a.created_at).getTime()
|
||||||
|
);
|
||||||
|
})"
|
||||||
|
:key="jobRun.id"
|
||||||
|
/>
|
||||||
|
</Accordion>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
</template>
|
19
resources/js/Components/ui/accordion/Accordion.vue
Normal file
19
resources/js/Components/ui/accordion/Accordion.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
AccordionRoot,
|
||||||
|
type AccordionRootEmits,
|
||||||
|
type AccordionRootProps,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from 'reka-ui'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionRootProps>()
|
||||||
|
const emits = defineEmits<AccordionRootEmits>()
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(props, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionRoot v-bind="forwarded">
|
||||||
|
<slot />
|
||||||
|
</AccordionRoot>
|
||||||
|
</template>
|
24
resources/js/Components/ui/accordion/AccordionContent.vue
Normal file
24
resources/js/Components/ui/accordion/AccordionContent.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { AccordionContent, type AccordionContentProps } from 'reka-ui'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionContentProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionContent
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
class="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||||
|
>
|
||||||
|
<div :class="cn('pb-4 pt-0', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</template>
|
24
resources/js/Components/ui/accordion/AccordionItem.vue
Normal file
24
resources/js/Components/ui/accordion/AccordionItem.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'reka-ui'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionItem
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="cn('border-b', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AccordionItem>
|
||||||
|
</template>
|
39
resources/js/Components/ui/accordion/AccordionTrigger.vue
Normal file
39
resources/js/Components/ui/accordion/AccordionTrigger.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { ChevronDown } from 'lucide-vue-next'
|
||||||
|
import {
|
||||||
|
AccordionHeader,
|
||||||
|
AccordionTrigger,
|
||||||
|
type AccordionTriggerProps,
|
||||||
|
} from 'reka-ui'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<AccordionTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AccordionHeader class="flex">
|
||||||
|
<AccordionTrigger
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<slot name="icon">
|
||||||
|
<ChevronDown
|
||||||
|
class="h-4 w-4 shrink-0 transition-transform duration-200"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
</AccordionTrigger>
|
||||||
|
</AccordionHeader>
|
||||||
|
</template>
|
4
resources/js/Components/ui/accordion/index.ts
Normal file
4
resources/js/Components/ui/accordion/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as Accordion } from './Accordion.vue'
|
||||||
|
export { default as AccordionContent } from './AccordionContent.vue'
|
||||||
|
export { default as AccordionItem } from './AccordionItem.vue'
|
||||||
|
export { default as AccordionTrigger } from './AccordionTrigger.vue'
|
35
resources/js/Components/ui/separator/Separator.vue
Normal file
35
resources/js/Components/ui/separator/Separator.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Separator, type SeparatorProps } from 'reka-ui'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<
|
||||||
|
SeparatorProps & { class?: HTMLAttributes['class'], label?: string }
|
||||||
|
>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Separator
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'shrink-0 bg-border relative',
|
||||||
|
props.orientation === 'vertical' ? 'w-px h-full' : 'h-px w-full',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="props.label"
|
||||||
|
:class="cn('text-xs text-muted-foreground bg-background absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex justify-center items-center',
|
||||||
|
props.orientation === 'vertical' ? 'w-[1px] px-1 py-2' : 'h-[1px] py-1 px-2',
|
||||||
|
)"
|
||||||
|
>{{ props.label }}</span>
|
||||||
|
</Separator>
|
||||||
|
</template>
|
1
resources/js/Components/ui/separator/index.ts
Normal file
1
resources/js/Components/ui/separator/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Separator } from './Separator.vue'
|
@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import JobForm from '../Components/Layout/Job/JobForm.vue'
|
import JobForm from '../Components/Layout/Job/JobForm.vue'
|
||||||
import JobCard from '../Components/Layout/Job/JobCard.vue'
|
import JobCard from '../Components/Layout/Job/JobCard.vue'
|
||||||
|
import JobRuns from '../Components/Layout/Job/JobRuns/JobRuns.vue';
|
||||||
import { Job } from "@/types/Jobs/job";
|
import { Job } from "@/types/Jobs/job";
|
||||||
import { Head } from "@inertiajs/vue3";
|
import { Head } from "@inertiajs/vue3";
|
||||||
|
|
||||||
@ -16,4 +17,6 @@ defineProps<{
|
|||||||
<JobCard :job="job" />
|
<JobCard :job="job" />
|
||||||
|
|
||||||
<JobForm :job="job" :error="error" />
|
<JobForm :job="job" :error="error" />
|
||||||
|
|
||||||
|
<JobRuns :job="job" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import { clsx, type ClassValue } from "clsx";
|
import type { Updater } from '@tanstack/vue-table'
|
||||||
import { twMerge } from "tailwind-merge";
|
import type { Ref } from 'vue'
|
||||||
|
import { type ClassValue, clsx } from 'clsx'
|
||||||
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) {
|
||||||
|
ref.value
|
||||||
|
= typeof updaterOrValue === 'function'
|
||||||
|
? updaterOrValue(ref.value)
|
||||||
|
: updaterOrValue
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function httpApi<T>(route: string): Promise<T> {
|
export async function httpApi<T>(route: string): Promise<T> {
|
||||||
|
14
resources/js/types/Jobs/job.d.ts
vendored
14
resources/js/types/Jobs/job.d.ts
vendored
@ -5,8 +5,9 @@ export type Job = {
|
|||||||
is_active: boolean;
|
is_active: boolean;
|
||||||
|
|
||||||
job_infos: JobInfo[];
|
job_infos: JobInfo[];
|
||||||
|
job_runs: JobRunArtifact[];
|
||||||
|
|
||||||
created_at: Date;
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type JobInfo = {
|
export type JobInfo = {
|
||||||
@ -26,16 +27,23 @@ export type JobInfo = {
|
|||||||
export type JobInfoType = {
|
export type JobInfoType = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
created_at: Date;
|
|
||||||
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type JobRunArtifact = {
|
export type JobRunArtifact = {
|
||||||
jobId: number;
|
id: number;
|
||||||
|
job_id: number;
|
||||||
artifacts: JobArtifact[];
|
artifacts: JobArtifact[];
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|
||||||
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type JobArtifact = {
|
export type JobArtifact = {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
|
||||||
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ Route::get('/jobs', function (Request $request) {
|
|||||||
|
|
||||||
Route::get('/test/{id}', function (Request $request, $id, BrowserJobsInstances $BrowserJobsInstances) {
|
Route::get('/test/{id}', function (Request $request, $id, BrowserJobsInstances $BrowserJobsInstances) {
|
||||||
$log = $BrowserJobsInstances->getJobInstance($id)->execute();
|
$log = $BrowserJobsInstances->getJobInstance($id)->execute();
|
||||||
|
dump($log);
|
||||||
return response()->json(['message' => 'Job ' . $id . ' ran', 'jobRun' => $log->load('artifacts')]);
|
return response()->json(['message' => 'Job ' . $id . ' ran', 'jobRun' => $log->load('artifacts')]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Browser\Jobs\Hellcase\HellcaseJob;
|
use App\Browser\Jobs\Hellcase\HellcaseJob;
|
||||||
|
use App\Browser\Jobs\HellcaseBattles\HellcaseBattlesJob;
|
||||||
use App\Jobs\PruneOldJobRuns;
|
use App\Jobs\PruneOldJobRuns;
|
||||||
use App\Services\BrowserJobsInstances;
|
use App\Services\BrowserJobsInstances;
|
||||||
use Illuminate\Foundation\Inspiring;
|
use Illuminate\Foundation\Inspiring;
|
||||||
@ -21,3 +22,4 @@ Schedule::job(new PruneOldJobRuns)->monthly()->onOneServer()->withoutOverlapping
|
|||||||
// Jobs
|
// Jobs
|
||||||
Schedule::job(new HellcaseJob)->daily()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
|
Schedule::job(new HellcaseJob)->daily()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
|
||||||
// Schedule::job(new HellcaseJob)->everyMinute()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
|
// Schedule::job(new HellcaseJob)->everyMinute()->onOneServer()->withoutOverlapping()->name('hellcase')->description('Hellcase job');
|
||||||
|
Schedule::job(new HellcaseBattlesJob)->hourly()->onOneServer()->withoutOverlapping()->name('hellcase_battles')->description('Hellcase battles job');
|
||||||
|
3
todo.md
3
todo.md
@ -13,6 +13,9 @@
|
|||||||
- Websocket installé
|
- Websocket installé
|
||||||
- Serveur php plus propre (nginx, apache, n'importe)
|
- Serveur php plus propre (nginx, apache, n'importe)
|
||||||
- Epic games
|
- Epic games
|
||||||
|
Pas l'air possible avec cloudflare
|
||||||
|
- Petit bug, quand l'on enregistre un formulaire avec une erreur, l'url a un argument GET ?error=mon%24erreur
|
||||||
|
Du coup dans la nav le job actuel n'est plus reconnu
|
||||||
|
|
||||||
## Pour deploy Lama
|
## Pour deploy Lama
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user