Skip to content

Commit

Permalink
feat: #249 improve 'services' page ux
Browse files Browse the repository at this point in the history
  • Loading branch information
bohdan-shulha committed Nov 2, 2024
1 parent 6237d24 commit 5ce928f
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 106 deletions.
3 changes: 3 additions & 0 deletions app/Actions/Services/StartDeployment.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ public function handle(User $user, Service $service, DeploymentData $deploymentD
$deployment = $service->deployments()->create([
'team_id' => $service->team_id,
'data' => $deploymentData,
'configured_by_id' => $user->id,
]);

$deployment->taskGroups()->attach($taskGroup);

$taskGroup->tasks()->createMany($deployment->asNodeTasks());

RebuildCaddy::run($service->team, $taskGroup, $deployment);
Expand Down
2 changes: 1 addition & 1 deletion app/Actions/Workers/ExecuteWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function handle(Service $service, Process $process, Worker $worker, ?Back
'type' => $taskGroupType,
'swarm_id' => $service->swarm_id,
'node_id' => $process->placementNodeId,
'invoker_id' => $deployment->latestTaskGroup->invoker_id,
'invoker_id' => $deployment->taskGroups()->latest('id')->first()->invoker_id,
'team_id' => $service->team_id,
]);

Expand Down
6 changes: 1 addition & 5 deletions app/Http/Controllers/ServiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ class ServiceController extends Controller
*/
public function index()
{
$services = Service::with(['latestDeployment' => function ($query) {
$query->with(['latestTaskGroup' => function ($query) {
$query->with('latestTask');
}]);
}])->orderBy('name')->get();
$services = Service::with('latestDeployment', 'latestDeployment.latestTaskGroup')->orderBy('name')->get();

$swarmExists = Swarm::exists();

Expand Down
10 changes: 5 additions & 5 deletions app/Models/Deployment.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Query\Builder as QueryBuilder;

Expand All @@ -22,6 +22,7 @@ class Deployment extends Model
'service_id',
'team_id',
'task_group_id',
'configured_by_id',
'data',
];

Expand All @@ -34,15 +35,14 @@ public function service(): BelongsTo
return $this->belongsTo(Service::class);
}

public function taskGroups(): HasManyThrough
public function taskGroups(): BelongsToMany
{
return $this->hasManyThrough(NodeTaskGroup::class, NodeTask::class, 'meta__deployment_id', 'id', 'id', 'task_group_id')->orderByDesc('id');
return $this->belongsToMany(NodeTaskGroup::class)->orderByDesc('id');
}

public function latestTaskGroup(): HasOneThrough
{
// FIXME: make sure that the "deployments" page displays only the actual daemon deployments. Keep "task launch" separate.
return $this->hasOneThrough(NodeTaskGroup::class, NodeTask::class, 'meta__deployment_id', 'id', 'id', 'task_group_id')->latest('id');
return $this->hasOneThrough(NodeTaskGroup::class, DeploymentNodeTaskGroup::class, 'deployment_id', 'id', 'id', 'node_task_group_id')->latest('id');
}

public function previousDeployment(): ?Deployment
Expand Down
10 changes: 10 additions & 0 deletions app/Models/DeploymentNodeTaskGroup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeploymentNodeTaskGroup extends Model
{
protected $table = 'deployment_node_task_group';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

use App\Models\NodeTaskGroupType;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('deployment_node_task_group', function (Blueprint $table) {
$table->id();
$table->foreignId('deployment_id')->constrained('deployments');
$table->foreignId('node_task_group_id')->constrained('node_task_groups');
});

$query = <<<'SQL'
INSERT INTO deployment_node_task_group (deployment_id, node_task_group_id)
SELECT DISTINCT meta__deployment_id, ntg.id
FROM node_tasks nt
INNER JOIN node_task_groups ntg ON ntg.id = nt.task_group_id
WHERE ntg.type IN (?, ?, ?) AND meta__deployment_id IS NOT NULL
ORDER BY ntg.id
SQL;

DB::insert($query, [NodeTaskGroupType::LaunchService->value, NodeTaskGroupType::CreateService->value, NodeTaskGroupType::UpdateService->value]);

Schema::table('node_tasks', function (Blueprint $table) {
$table->dropColumn('meta__deployment_id');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::drop('deployment_node_task_group');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

use App\Models\NodeTaskGroupType;
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
{
Schema::table('deployments', function (Blueprint $table) {
$table->foreignId('configured_by_id')->constrained('users');
});

$query = <<<'SQL'
UPDATE deployments d
SET configured_by_id = ntg.invoker_id
FROM deployment_node_task_group dntg
INNER JOIN node_task_groups ntg ON ntg.id = dntg.node_task_group_id
WHERE dntg.deployment_id = d.id AND ntg.type IN (?, ?, ?)
SQL;

DB::update($query, [NodeTaskGroupType::LaunchService->value, NodeTaskGroupType::CreateService->value, NodeTaskGroupType::UpdateService->value]);
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('deployments', function (Blueprint $table) {
$table->dropColumn('configured_by_id');
});
}
};
148 changes: 77 additions & 71 deletions resources/js/Components/NodeTasks/TaskResult.vue
Original file line number Diff line number Diff line change
@@ -1,84 +1,90 @@
<script setup>
import {computed, reactive} from "vue";
import { computed, reactive } from "vue";
import TaskStatus from "./TaskStatus.vue";
const props = defineProps({
'task': Object,
})
task: Object,
});
const state = reactive({
expanded: props.task.status === 'failed',
})
expanded: props.task.status === "failed",
});
const classes = computed(() => {
const modifier = props.task.status === 'failed' ? 'text-red-600 dark:text-red-500' : 'text-green-600 dark:text-green-500';
const modifier =
props.task.status === "failed"
? "text-red-600 dark:text-red-500"
: "text-green-600 dark:text-green-500";
const base = 'px-4 py-2 flex items-center select-none ' + modifier;
const base = "px-4 py-2 flex items-center select-none " + modifier;
if (props.task.result) {
return base + " hover:cursor-pointer hover:bg-gray-50";
}
if (props.task.result) {
return base + ' hover:cursor-pointer hover:bg-gray-50';
}
return base;
})
return base;
});
</script>

<template>
<li
v-auto-animate="{duration: 100}"
class="w-full border-b border-gray-200 first:rounded-t-lg last:rounded-b-lg dark:border-gray-600 overflow-hidden">
<div :class="classes"
@click="state.expanded = ! state.expanded && $props.task.result">
<svg
v-if="task.status === 'pending'"
class="w-4 h-4 me-2 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
</svg>

<svg
v-if="task.status === 'running'"
class="w-4 h-4 me-2 animate-rotate text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.651 7.65a7.131 7.131 0 0 0-12.68 3.15M18.001 4v4h-4m-7.652 8.35a7.13 7.13 0 0 0 12.68-3.15M6 20v-4h4"/>
</svg>

<svg
v-if="task.status === 'completed'"
class="w-4 h-4 me-2 text-green-600 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm13.707-1.293a1 1 0 0 0-1.414-1.414L11 12.586l-1.793-1.793a1 1 0 0 0-1.414 1.414l2.5 2.5a1 1 0 0 0 1.414 0l4-4Z" clip-rule="evenodd"/>
</svg>

<svg
v-if="task.status === 'failed'"
class="w-4 h-4 me-2 text-red-600 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm7.707-3.707a1 1 0 0 0-1.414 1.414L10.586 12l-2.293 2.293a1 1 0 1 0 1.414 1.414L12 13.414l2.293 2.293a1 1 0 0 0 1.414-1.414L13.414 12l2.293-2.293a1 1 0 0 0-1.414-1.414L12 10.586 9.707 8.293Z" clip-rule="evenodd"/>
</svg>

<svg v-if="task.status === 'canceled'"
class="w-4 h-4 me-2 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="m6 6 12 12m3-6a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
</svg>



<span v-html="task.formatted_meta" class="grow" />

<span class="text-xs me-2 text-gray-500">#{{ task.id }}</span>

<span v-auto-animate="{duration: 100}" v-if="task.result">
<svg v-if="state.expanded"
class="w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m16 14-4-4-4 4"/>
</svg>
<svg
v-else
class="w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8 10 4 4 4-4"/>
</svg>

</span>
<span v-else class="w-4"></span>
</div>
<div v-if="state.expanded" class="px-4 py-2 border-t border-t-gray-100 bg-gray-50" v-html="task.formatted_result" />
</li>
</template>
<li
v-auto-animate="{ duration: 100 }"
class="w-full border-b border-gray-200 first:rounded-t-lg last:rounded-b-lg dark:border-gray-600 overflow-hidden"
>
<div
:class="classes"
@click="state.expanded = !state.expanded && $props.task.result"
>
<TaskStatus :task-status="task.status" />

<span v-html="task.formatted_meta" class="grow" />

<span class="text-xs me-2 text-gray-500">#{{ task.id }}</span>

<span v-auto-animate="{ duration: 100 }" v-if="task.result">
<svg
v-if="state.expanded"
class="w-4 h-4 text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m16 14-4-4-4 4"
/>
</svg>
<svg
v-else
class="w-4 h-4 text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m8 10 4 4 4-4"
/>
</svg>
</span>
<span v-else class="w-4"></span>
</div>
<div
v-if="state.expanded"
class="px-4 py-2 border-t border-t-gray-100 bg-gray-50"
v-html="task.formatted_result"
/>
</li>
</template>
Loading

0 comments on commit 5ce928f

Please sign in to comment.