--wip-- [skip ci]

This commit is contained in:
philipp lang 2024-08-01 18:25:25 +02:00
parent 310f3c9727
commit 3e92a7c40a
19 changed files with 222 additions and 78 deletions

View File

@ -30,17 +30,4 @@ class FormSettings extends LocalSettings implements Storeable
'clearCacheUrl' => 'present|string', 'clearCacheUrl' => 'present|string',
]; ];
} }
/**
* @inheritdoc
*/
public function viewData(): array
{
return [
'data' => [
'registerUrl' => $this->registerUrl,
'clearCacheUrl' => $this->clearCacheUrl,
]
];
}
} }

View File

@ -2,6 +2,7 @@
namespace App\Http\Resources; namespace App\Http\Resources;
use App\Lib\HasMeta;
use App\User; use App\User;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
use Storage; use Storage;
@ -11,6 +12,8 @@ use Storage;
*/ */
class UserResource extends JsonResource class UserResource extends JsonResource
{ {
use HasMeta;
/** /**
* Transform the resource into an array. * Transform the resource into an array.
* *
@ -25,9 +28,17 @@ class UserResource extends JsonResource
'lastname' => $this->lastname, 'lastname' => $this->lastname,
'avatar_url' => $this->getGravatarUrl(), 'avatar_url' => $this->getGravatarUrl(),
'email' => $this->email, 'email' => $this->email,
'id' => $this->id,
'avatar' => [ 'avatar' => [
'src' => Storage::url('avatar.png'), 'src' => Storage::url('avatar.png'),
], ],
]; ];
} }
public static function meta(): array
{
return [
'links' => []
];
}
} }

View File

@ -4,7 +4,6 @@ namespace App\Invoice;
use App\Setting\Contracts\Storeable; use App\Setting\Contracts\Storeable;
use App\Setting\LocalSettings; use App\Setting\LocalSettings;
use Lorisleiva\Actions\ActionRequest;
class InvoiceSettings extends LocalSettings implements Storeable class InvoiceSettings extends LocalSettings implements Storeable
{ {
@ -35,28 +34,6 @@ class InvoiceSettings extends LocalSettings implements Storeable
return 'bill'; return 'bill';
} }
/**
* @inheritdoc
*/
public function viewData(): array
{
return [
'data' => [
'from_long' => $this->from_long,
'from' => $this->from,
'mobile' => $this->mobile,
'email' => $this->email,
'website' => $this->website,
'address' => $this->address,
'place' => $this->place,
'zip' => $this->zip,
'iban' => $this->iban,
'bic' => $this->bic,
'rememberWeeks' => $this->rememberWeeks,
]
];
}
/** /**
* @inheritdoc * @inheritdoc
*/ */

View File

@ -21,10 +21,16 @@ class MailgatewaySettings extends LocalSettings
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function viewData(): array public function meta(): array
{ {
return [ return MailgatewayResource::meta();
'data' => MailgatewayResource::collection(Mailgateway::paginate(10)), }
];
/**
* @inheritdoc
*/
public function data()
{
return MailgatewayResource::collection(Mailgateway::paginate(10));
} }
} }

View File

@ -41,13 +41,11 @@ class ModuleSettings extends LocalSettings implements Storeable
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function viewData(): array public function meta(): array
{ {
return [ return [
'data' => [ ...parent::meta(),
'modules' => $this->modules, 'modules' => Module::forSelect()
],
'meta' => ['modules' => Module::forSelect()],
]; ];
} }
} }

View File

@ -38,16 +38,4 @@ class PreventionSettings extends LocalSettings implements Storeable
'formmail' => EditorData::from($request->formmail), 'formmail' => EditorData::from($request->formmail),
]; ];
} }
/**
* @inheritdoc
*/
public function viewData(): array
{
return [
'data' => [
'formmail' => $this->formmail,
]
];
}
} }

View File

@ -18,9 +18,9 @@ class ViewAction
session()->put('title', $settingGroup::title()); session()->put('title', $settingGroup::title());
return Inertia::render('setting/' . ucfirst($settingGroup::group()), [ return Inertia::render('setting/' . ucfirst($settingGroup::group()), [
...$settingGroup->viewData(), 'data' => $settingGroup->data(),
'settingMenu' => app(SettingFactory::class)->getShare(), 'settingMenu' => app(SettingFactory::class)->getShare(),
'storeUrl' => $settingGroup->storeUrl(), 'meta' => $settingGroup->meta(),
]); ]);
} }
} }

View File

@ -19,10 +19,22 @@ abstract class LocalSettings extends Settings
return $this->url(); return $this->url();
} }
public function meta(): array
{
return [
'links' => [
'store' => $this->storeUrl(),
]
];
}
/** /**
* @return array<string, mixed> * @return mixed
*/ */
abstract public function viewData(): array; public function data()
{
return $this->toArray();
}
public function beforeSave(ActionRequest $request): void public function beforeSave(ActionRequest $request): void
{ {

View File

@ -4,7 +4,6 @@ namespace App\Setting;
use App\Group; use App\Group;
use App\Initialize\Actions\NamiLoginCheckAction; use App\Initialize\Actions\NamiLoginCheckAction;
use App\Nami\Actions\SettingSaveAction;
use App\Setting\Contracts\Storeable; use App\Setting\Contracts\Storeable;
use Lorisleiva\Actions\ActionRequest; use Lorisleiva\Actions\ActionRequest;
use Zoomyboy\LaravelNami\Api; use Zoomyboy\LaravelNami\Api;
@ -64,14 +63,12 @@ class NamiSettings extends LocalSettings implements Storeable
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function viewData(): array public function data(): array
{ {
return [ return [
'data' => [
'mglnr' => $this->mglnr, 'mglnr' => $this->mglnr,
'password' => '', 'password' => '',
'default_group_id' => $this->default_group_id, 'default_group_id' => $this->default_group_id,
]
]; ];
} }
} }

View File

@ -10,6 +10,7 @@ use App\Module\ModuleSettings;
use App\Prevention\PreventionSettings; use App\Prevention\PreventionSettings;
use App\Setting\Actions\StoreAction; use App\Setting\Actions\StoreAction;
use App\Setting\Actions\ViewAction; use App\Setting\Actions\ViewAction;
use App\User\UserSettings;
use Illuminate\Routing\Router; use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
@ -43,5 +44,6 @@ class SettingServiceProvider extends ServiceProvider
app(SettingFactory::class)->register(FormSettings::class); app(SettingFactory::class)->register(FormSettings::class);
app(SettingFactory::class)->register(FileshareSettings::class); app(SettingFactory::class)->register(FileshareSettings::class);
app(SettingFactory::class)->register(PreventionSettings::class); app(SettingFactory::class)->register(PreventionSettings::class);
app(SettingFactory::class)->register(UserSettings::class);
} }
} }

View File

@ -0,0 +1,18 @@
<?php
namespace App\User\Actions;
use App\Http\Resources\UserResource;
use App\User;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Lorisleiva\Actions\Concerns\AsAction;
class IndexAction
{
use AsAction;
public function handle(): AnonymousResourceCollection
{
return UserResource::collection(User::orderByRaw('lastname, firstname')->get());
}
}

36
app/User/UserSettings.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace App\User;
use App\Http\Resources\UserResource;
use App\Setting\LocalSettings;
use App\User;
class UserSettings extends LocalSettings
{
public static function group(): string
{
return 'user';
}
public static function title(): string
{
return 'Benutzer';
}
/**
* @inheritdoc
*/
public function meta(): array
{
return UserResource::meta();
}
/**
* @inheritdoc
*/
public function data()
{
return UserResource::collection(User::orderByRaw('lastname, firstname')->get())->toArray(request());
}
}

5
bin/copydbdk Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
ssh dkd 'cd $ADREMA_PATH && docker compose exec db mysqldump -q -udb -p'$ADREMA_DB_PASSWORD' db --ignore-table=db.telescope_entries --ignore-table=db.failed_jobs' > db.tmp
sudo mysql scoutrobot < db.tmp

7
bin/testmigration Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/zsh
php artisan migrate:fresh --env=testing
ssh dkd 'cd $ADREMA_PATH && docker compose exec db mysqldump -q -udb -p'$ADREMA_DB_PASSWORD' db --ignore-table=db.telescope_entries --ignore-table=db.failed_jobs' > dddd.sql
sudo mysql scoutrobottest < dddd.sql
php artisan migrate --env=testing

View File

@ -92,6 +92,8 @@ services:
socketi: socketi:
image: quay.io/soketi/soketi:89604f268623cf799573178a7ba56b7491416bde-16-debian image: quay.io/soketi/soketi:89604f268623cf799573178a7ba56b7491416bde-16-debian
ports:
- '6001:6001'
environment: environment:
SOKETI_DEFAULT_APP_ID: adremaid SOKETI_DEFAULT_APP_ID: adremaid
SOKETI_DEFAULT_APP_KEY: adremakey SOKETI_DEFAULT_APP_KEY: adremakey
@ -104,6 +106,8 @@ services:
meilisearch: meilisearch:
image: getmeili/meilisearch:v1.6 image: getmeili/meilisearch:v1.6
ports:
- '7700:7700'
volumes: volumes:
- ./data/meilisearch:/meili_data - ./data/meilisearch:/meili_data
env_file: env_file:

View File

@ -2,9 +2,11 @@ import {computed, ref, inject, onBeforeUnmount} from 'vue';
import {router} from '@inertiajs/vue3'; import {router} from '@inertiajs/vue3';
import useQueueEvents from './useQueueEvents.js'; import useQueueEvents from './useQueueEvents.js';
export function useIndex(props, siteName) { export function useIndex(props, siteName = null) {
const axios = inject('axios'); const axios = inject('axios');
const {startListener, stopListener} = useQueueEvents(siteName, () => reload(false)); if (siteName !== null) {
var {startListener, stopListener} = useQueueEvents(siteName, () => reload(false));
}
const single = ref(null); const single = ref(null);
const rawProps = JSON.parse(JSON.stringify(props)); const rawProps = JSON.parse(JSON.stringify(props));
const inner = { const inner = {
@ -86,8 +88,10 @@ export function useIndex(props, siteName) {
reload(true); reload(true);
} }
if (siteName !== null) {
startListener(); startListener();
onBeforeUnmount(() => stopListener()); onBeforeUnmount(() => stopListener());
}
return { return {
data: inner.data, data: inner.data,

View File

@ -0,0 +1,61 @@
<template>
<page-layout>
<template #toolbar>
<page-toolbar-button color="primary" icon="plus" @click.prevent="create">Neuer Benutzer</page-toolbar-button>
</template>
<ui-popup v-if="single !== null" :heading="single.id ? 'Benutzer bearbeiten' : 'Neuer Benutzer'" @close="cancel">
<form @submit.prevent="submit">
<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>Nachname</th>
<th>Vorname</th>
<th>Aktion</th>
</thead>
<tr v-for="(user, index) in data" :key="index">
<td v-text="user.lastname"></td>
<td v-text="user.firstname"></td>
<td>
<a v-tooltip="`Bearbeiten`" href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(user)"><ui-sprite src="pencil"></ui-sprite></a>
<a v-tooltip="`Löschen`" href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(user)"><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 lang="js" setup>
import SettingLayout from '../setting/Layout.vue';
const props = defineProps({
data: {
type: Object,
required: true,
},
meta: {
type: Object,
required: false,
default: () => {
return {};
},
},
});
import {indexProps, useIndex} from '../../composables/useInertiaApiIndex.js';
const {data, meta, single, create} = useIndex(props);
</script>

View File

@ -1,11 +1,11 @@
import {useIndex} from '../../composables/useIndex.js'; import {useIndex} from '../../composables/useInertiaApiIndex.js';
import SettingLayout from './Layout.vue'; import SettingLayout from './Layout.vue';
export function useSettings(props) { export function useSettings(props) {
const {data, meta, router} = useIndex(props); const {data, meta, router} = useIndex(props);
function submit() { function submit() {
router.post(props.storeUrl, {...data.value}); router.post(meta.value.links.store, {...data.value});
} }
return { return {
@ -22,10 +22,6 @@ const props = {
type: Object, type: Object,
required: true, required: true,
}, },
storeUrl: {
type: String,
required: true,
},
settingMenu: { settingMenu: {
type: Object, type: Object,
required: true, required: true,

View File

@ -0,0 +1,35 @@
<?php
namespace Tests\Feature\Permission;
use App\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class UserIndexTest extends TestCase
{
use DatabaseTransactions;
public function testItOpensSettingsPage(): void
{
$this->login()->loginNami();
$this->get(route('setting.view', ['settingGroup' => 'user']))
->assertOk()
->assertComponent('setting/User');
}
public function testItListsUsers(): void
{
$this->login()->loginNami();
auth()->user()->update(['firstname' => 'Jane', 'lastname' => 'Doe']);
User::factory()->create(['firstname' => 'John', 'lastname' => 'Doe']);
$anna = User::factory()->create(['firstname' => 'Anna', 'lastname' => 'Doe']);
$this->get(route('api.user.index'))
->assertJsonPath('data.0.firstname', 'Anna')
->assertJsonPath('data.0.lastname', 'Doe')
->assertJsonPath('data.0.id', $anna->id)
->assertJsonPath('data.1.firstname', 'Jane')
->assertJsonPath('data.2.firstname', 'John');
}
}