Add: Store connection frontend

This commit is contained in:
philipp lang 2024-06-27 11:35:10 +02:00
parent 32e7f8c41d
commit e9e0be83cc
10 changed files with 172 additions and 10 deletions

View File

@ -8,7 +8,7 @@ use Inertia\Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
class IndexAction
class FileshareConnectionIndexAction
{
use AsAction;
@ -17,7 +17,7 @@ class IndexAction
session()->put('menu', 'setting');
session()->put('title', 'Datei-Verbindungen');
return Inertia::render('fileshare/Index', [
return Inertia::render('fileshareconnection/Index', [
'data' => FileshareConnectionResource::collection(FileshareConnection::paginate(15)),
]);
}

View File

@ -7,7 +7,7 @@ use Illuminate\Validation\ValidationException;
use Lorisleiva\Actions\ActionRequest;
use Lorisleiva\Actions\Concerns\AsAction;
class FileshareStoreAction
class FileshareConnectionStoreAction
{
use AsAction;

View File

@ -7,4 +7,29 @@ use Spatie\LaravelData\Data;
abstract class ConnectionType extends Data
{
abstract public function check(): bool;
/**
* @return array<string, mixed>
*/
abstract public static function defaults(): array;
abstract public static function title(): string;
/**
* @return array<int, array{label: string, key: string, type: string}>
*/
abstract public static function fields(): array;
/**
* @return array<int, mixed>
*/
public static function forSelect(): array
{
return collect(glob(base_path('app/Fileshare/ConnectionTypes/*')))
->map(fn ($file) => 'App\\Fileshare\\ConnectionTypes\\' . pathinfo($file, PATHINFO_FILENAME))
->filter(fn ($file) => $file !== static::class)
->values()
->map(fn ($file) => ['id' => $file, 'name' => $file::title(), 'defaults' => $file::defaults(), 'fields' => $file::fields()])
->toArray();
}
}

View File

@ -29,4 +29,33 @@ class OwncloudConnection extends ConnectionType
return false;
}
}
/**
* @inheritdoc
*/
public static function defaults(): array
{
return [
'user' => '',
'password' => '',
'base_url' => '',
];
}
public static function title(): string
{
return 'Owncloud';
}
/**
* @inheritdoc
*/
public static function fields(): array
{
return [
['label' => 'URL', 'key' => 'base_url', 'type' => 'text'],
['label' => 'Benutzer', 'key' => 'user', 'type' => 'text'],
['label' => 'Passwort', 'key' => 'password', 'type' => 'password'],
];
}
}

View File

@ -2,7 +2,7 @@
namespace App\Fileshare;
use App\Fileshare\Actions\IndexAction;
use App\Fileshare\Actions\FileshareConnectionIndexAction;
use App\Setting\Contracts\Indexable;
use App\Setting\LocalSettings;
@ -20,7 +20,7 @@ class FileshareSettings extends LocalSettings implements Indexable
public static function indexAction(): string
{
return IndexAction::class;
return FileshareConnectionIndexAction::class;
}
public static function title(): string

View File

@ -2,7 +2,9 @@
namespace App\Fileshare\Resources;
use App\Fileshare\ConnectionTypes\ConnectionType;
use App\Fileshare\Models\FileshareConnection;
use App\Lib\HasMeta;
use Illuminate\Http\Resources\Json\JsonResource;
/**
@ -10,6 +12,9 @@ use Illuminate\Http\Resources\Json\JsonResource;
*/
class FileshareConnectionResource extends JsonResource
{
use HasMeta;
/**
* Transform the resource into an array.
*
@ -24,6 +29,25 @@ class FileshareConnectionResource extends JsonResource
'type' => get_class($this->type),
'config' => $this->type->toArray(),
'id' => $this->id,
'type_human' => $this->type::title(),
];
}
/**
* @return array<string, mixed>
*/
public static function meta(): array
{
return [
'default' => [
'name' => '',
'type' => null,
'config' => null,
],
'types' => ConnectionType::forSelect(),
'links' => [
'store' => route('fileshare.store'),
]
];
}
}

View File

@ -0,0 +1,77 @@
<template>
<page-layout>
<template #toolbar>
<page-toolbar-button color="primary" icon="plus" @click.prevent="create">Neue Verbindung</page-toolbar-button>
</template>
<ui-popup v-if="single !== null" :heading="single.id ? 'Verbindung bearbeiten' : 'Neue Verbindung'" @close="cancel">
<form @submit.prevent="submit">
<section class="grid grid-cols-2 gap-3 mt-6">
<f-text id="name" v-model="single.name" name="name" label="Bezeichnung" required></f-text>
<f-select id="type" :model-value="single.type" label="Typ" name="type" :options="meta.types" required
@update:model-value="
single = {
...single,
type: $event,
config: { ...getType($event).defaults },
}
"></f-select>
<template v-for="(field, index) in getType(single.type).fields">
<f-text v-if="field.type === 'text' || field.type === 'password' || field.type === 'email'"
:id="field.key" :key="index" v-model="single.config[field.key]" :label="field.label"
:type="field.type" :name="field.key" required></f-text>
</template>
</section>
<section class="flex mt-4 space-x-2">
<ui-button type="submit" class="btn-danger">Speichern</ui-button>
<ui-button class="btn-primary" @click.prevent="single = null">Abbrechen</ui-button>
</section>
</form>
</ui-popup>
<setting-layout>
<div class="w-full h-full pb-6">
<table cellspacing="0" cellpadding="0" border="0" class="custom-table custom-table-sm hidden md:table">
<thead>
<th>Bezeichnung</th>
<th>Typ</th>
<th>Prüfung</th>
<th>Aktion</th>
</thead>
<tr v-for="(connection, index) in data" :key="index">
<td v-text="connection.name"></td>
<td v-text="connection.type_human"></td>
<td>
<ui-boolean-display :value="connection.is_active" long-label="Verbindungsstatus"
:label="connection.is_active ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen'"></ui-boolean-display>
</td>
<td>
<a v-tooltip="`Bearbeiten`" href="#" class="inline-flex btn btn-warning btn-sm"
@click.prevent="edit(connection)"><ui-sprite src="pencil"></ui-sprite></a>
</td>
</tr>
</table>
<div class="px-6">
<ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
</div>
</div>
</setting-layout>
</page-layout>
</template>
<script setup>
import { indexProps, useIndex } from '../../composables/useInertiaApiIndex.js';
import SettingLayout from '../setting/Layout.vue';
const props = defineProps(indexProps);
const { meta, data, create, edit, cancel, single, submit } = useIndex(props.data, 'mailgateway');
function getType(type) {
if (!type) {
return {
fields: [],
};
}
return meta.value.types.find((t) => t.id === type);
}
</script>

View File

@ -19,7 +19,7 @@ use App\Invoice\Actions\InvoiceStoreAction;
use App\Course\Actions\CourseUpdateAction;
use App\Dashboard\Actions\IndexAction as DashboardIndexAction;
use App\Efz\ShowEfzDocumentAction;
use App\Fileshare\Actions\FileshareStoreAction;
use App\Fileshare\Actions\FileshareConnectionStoreAction;
use App\Form\Actions\ExportAction as ActionsExportAction;
use App\Form\Actions\FormDestroyAction;
use App\Form\Actions\FormIndexAction;
@ -169,5 +169,5 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::delete('/participant/{participant}', ParticipantDestroyAction::class)->name('participant.destroy');
// ------------------------------------ fileshare -----------------------------------
Route::post('/fileshare', FileshareStoreAction::class)->name('fileshare.store');
Route::post('/fileshare', FileshareConnectionStoreAction::class)->name('fileshare.store');
});

View File

@ -6,7 +6,7 @@ use App\Fileshare\ConnectionTypes\OwncloudConnection;
use App\Fileshare\Models\FileshareConnection;
use Tests\FileshareTestCase;
class IndexActionTest extends FileshareTestCase
class FileshareConnectionIndexActionTest extends FileshareTestCase
{
public function testItListsOwncloudConnectionsThatAreActive(): void
{
@ -23,6 +23,13 @@ class IndexActionTest extends FileshareTestCase
->assertInertiaPath('data.data.0.config.password', 'secret')
->assertInertiaPath('data.data.0.config.base_url', env('TEST_OWNCLOUD_DOMAIN'))
->assertInertiaPath('data.data.0.id', $connection->id)
->assertInertiaPath('data.data.0.is_active', true);
->assertInertiaPath('data.data.0.is_active', true)
->assertInertiaPath('data.data.0.type_human', 'Owncloud')
->assertInertiaPath('data.meta.default.name', '')
->assertInertiaPath('data.meta.links.store', route('fileshare.store'))
->assertInertiaPath('data.meta.types.0.id', OwncloudConnection::class)
->assertInertiaPath('data.meta.types.0.name', 'Owncloud')
->assertInertiaPath('data.meta.types.0.defaults.base_url', '')
->assertInertiaPath('data.meta.types.0.fields.1', ['label' => 'Benutzer', 'key' => 'user', 'type' => 'text']);
}
}

View File

@ -6,7 +6,7 @@ use App\Fileshare\ConnectionTypes\OwncloudConnection;
use App\Fileshare\Models\FileshareConnection;
use Tests\FileshareTestCase;
class StoreActionTest extends FileshareTestCase
class FileshareConnectionStoreActionTest extends FileshareTestCase
{
public function testItStoresAConnection(): void
{