--wip-- [skip ci]
This commit is contained in:
parent
50878a9a3c
commit
33149e8b79
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Member\Data;
|
namespace App\Member\Data;
|
||||||
|
|
||||||
|
use App\Group;
|
||||||
use Spatie\LaravelData\Data;
|
use Spatie\LaravelData\Data;
|
||||||
use Spatie\LaravelData\Attributes\MapInputName;
|
use Spatie\LaravelData\Attributes\MapInputName;
|
||||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||||
|
@ -16,4 +17,13 @@ class GroupData extends Data {
|
||||||
public string $name,
|
public string $name,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public static function fromId(int $id): static {
|
||||||
|
$group = Group::findOrFail($id);
|
||||||
|
|
||||||
|
return static::from([
|
||||||
|
'name' => $group->name,
|
||||||
|
'id' => $group->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,78 +3,44 @@
|
||||||
namespace App\Member\Data;
|
namespace App\Member\Data;
|
||||||
|
|
||||||
use Spatie\LaravelData\Data;
|
use Spatie\LaravelData\Data;
|
||||||
use Spatie\LaravelData\Attributes\MapInputName;
|
|
||||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
|
||||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
|
||||||
use App\Member\Membership;
|
use App\Member\Membership;
|
||||||
use App\Member\Member;
|
use App\Member\Member;
|
||||||
use App\Activity;
|
|
||||||
use App\Lib\Data\DateData;
|
use App\Lib\Data\DateData;
|
||||||
|
|
||||||
#[MapInputName(SnakeCaseMapper::class)]
|
|
||||||
#[MapOutputName(SnakeCaseMapper::class)]
|
|
||||||
class MembershipData extends Data
|
class MembershipData extends Data
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public int $id,
|
public ?ActivityData $activity = null,
|
||||||
public ActivityData $activity,
|
public ?SubactivityData $subactivity = null,
|
||||||
public SubactivityData $subactivity,
|
public ?GroupData $group = null,
|
||||||
public GroupData $group,
|
public ?DateData $promisedAt = null,
|
||||||
public ?DateData $promisedAt,
|
public ?DateData $from = null,
|
||||||
public DateData $from,
|
public bool $isActive = false,
|
||||||
public bool $isActive,
|
public array $links = [],
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public static function fromModel(Membership $membership): static
|
public static function fromModel(Membership $membership): static
|
||||||
{
|
{
|
||||||
return static::factory()->withoutMagicalCreation()->from([
|
return static::factory()->withoutMagicalCreation()->from([
|
||||||
'id' => $membership->id,
|
|
||||||
'activity' => $membership->activity,
|
'activity' => $membership->activity,
|
||||||
'subactivity' => $membership->subactivity,
|
'subactivity' => $membership?->subactivity,
|
||||||
'is_active' => $membership->isActive(),
|
'isActive' => $membership->isActive(),
|
||||||
'from' => $membership->from,
|
'from' => $membership->from,
|
||||||
'group' => $membership->group,
|
'group' => $membership->group,
|
||||||
'promised_at' => $membership->promised_at,
|
'promisedAt' => $membership->promised_at,
|
||||||
|
'links' => [
|
||||||
|
'update' => route('membership.update', $membership),
|
||||||
|
'destroy' => route('membership.destroy', $membership),
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function with(): array
|
public static function memberMeta(Member $member): MembershipMeta
|
||||||
{
|
{
|
||||||
return [
|
return MembershipMeta::fromMember($member);
|
||||||
// 'human_date' => $this->from->format('d.m.Y'),
|
|
||||||
// 'promised_at_human' => $this->promisedAt?->format('d.m.Y'),
|
|
||||||
// 'promised_at' => $this->promisedAt?->format('Y-m-d'),
|
|
||||||
'links' => [
|
|
||||||
'update' => route('membership.update', ['membership' => $this->id]),
|
|
||||||
'destroy' => route('membership.destroy', ['membership' => $this->id]),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function memberMeta(Member $member): array
|
|
||||||
{
|
|
||||||
$activities = Activity::with('subactivities')->get();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'links' => [
|
|
||||||
'store' => route('member.membership.store', ['member' => $member]),
|
|
||||||
],
|
|
||||||
'groups' => NestedGroup::cacheForSelect(),
|
|
||||||
'activities' => $activities->map(fn($activity) => ['id' => $activity->id, 'name' => $activity->name]),
|
|
||||||
'subactivities' => $activities->mapWithKeys(fn($activity) => [$activity->id => $activity->subactivities->map(fn($subactivity) => ['id' => $subactivity->id, 'name' => $subactivity->name, 'is_age_group' => $subactivity->is_age_group])]),
|
|
||||||
'default' => [
|
|
||||||
'group_id' => $member->group_id,
|
|
||||||
'activity_id' => null,
|
|
||||||
'subactivity_id' => null,
|
|
||||||
'promised_at' => null,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Member\Data;
|
||||||
|
|
||||||
|
use App\Activity;
|
||||||
|
use Spatie\LaravelData\Data;
|
||||||
|
use Spatie\LaravelData\Attributes\MapInputName;
|
||||||
|
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||||
|
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||||
|
use App\Member\Member;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
#[MapInputName(SnakeCaseMapper::class)]
|
||||||
|
#[MapOutputName(SnakeCaseMapper::class)]
|
||||||
|
class MembershipMeta extends Data
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public array $links,
|
||||||
|
public Collection $groups,
|
||||||
|
public Collection $activities,
|
||||||
|
public Collection $subactivities,
|
||||||
|
public MembershipData $default,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fromMember(Member $member): static
|
||||||
|
{
|
||||||
|
$activities = Activity::with('subactivities')->get();
|
||||||
|
|
||||||
|
return static::factory()->withoutMagicalCreation()->from([
|
||||||
|
'links' => [
|
||||||
|
'store' => route('member.membership.store', ['member' => $member]),
|
||||||
|
],
|
||||||
|
'groups' => NestedGroup::cacheForSelect(),
|
||||||
|
'activities' => $activities->map(fn($activity) => ['id' => $activity->id, 'name' => $activity->name]),
|
||||||
|
'subactivities' => $activities->mapWithKeys(fn($activity) => [$activity->id => $activity->subactivities->map(fn($subactivity) => ['id' => $subactivity->id, 'name' => $subactivity->name, 'is_age_group' => $subactivity->is_age_group])]),
|
||||||
|
'default' => MembershipData::from(['group' => $member->group_id]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -103,6 +103,8 @@ services:
|
||||||
- ./data/redis:/data
|
- ./data/redis:/data
|
||||||
|
|
||||||
meilisearch:
|
meilisearch:
|
||||||
|
ports:
|
||||||
|
- "7700:7700"
|
||||||
image: getmeili/meilisearch:v1.6
|
image: getmeili/meilisearch:v1.6
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/meilisearch:/meili_data
|
- ./data/meilisearch:/meili_data
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<label class="flex flex-col group" :for="id" :class="sizeClass(size)">
|
<label class="flex flex-col group" :for="id" :class="sizeClass(size)">
|
||||||
<f-label v-if="label" :required="required" :value="label"></f-label>
|
<f-label v-if="label" :required="required" :value="label" />
|
||||||
<div class="relative flex-none flex">
|
<div class="relative flex-none flex">
|
||||||
<select v-model="inner" :disabled="disabled" :name="name" :class="[fieldHeight, fieldAppearance, selectAppearance]">
|
<select v-model="inner" :disabled="disabled" :name="name" :class="[fieldHeight, fieldAppearance, selectAppearance]">
|
||||||
<option v-if="placeholder" :value="def">{{ placeholder }}</option>
|
<option v-if="placeholder" :value="def">{{ placeholder }}</option>
|
||||||
<option v-for="option in parsedOptions" :key="option.id" :value="option.id">{{ option.name }}</option>
|
<option v-for="option in parsedOptions" :key="option.id" :value="option.id">{{ option.name }}</option>
|
||||||
</select>
|
</select>
|
||||||
<f-hint v-if="hint" :value="hint"></f-hint>
|
<f-hint v-if="hint" :value="hint" />
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script lang="ts" setup>
|
||||||
import {computed, ref} from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
import useFieldSize from '../../composables/useFieldSize.js';
|
import useFieldSize from '../../composables/useFieldSize.js';
|
||||||
import map from 'lodash/map';
|
import map from 'lodash/map';
|
||||||
|
@ -59,11 +59,6 @@ const props = defineProps({
|
||||||
default: '--kein--',
|
default: '--kein--',
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
def: {
|
|
||||||
required: false,
|
|
||||||
type: Number,
|
|
||||||
default: -1,
|
|
||||||
},
|
|
||||||
name: {
|
name: {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
@ -79,8 +74,8 @@ const parsedOptions = computed(() => {
|
||||||
return Array.isArray(props.options)
|
return Array.isArray(props.options)
|
||||||
? props.options
|
? props.options
|
||||||
: map(props.options, (value, key) => {
|
: map(props.options, (value, key) => {
|
||||||
return {name: value, id: key};
|
return {name: value, id: key};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const def = ref('iu1Feixah5AeKai3ewooJahjeaegee0eiD4maeth1oul4Hei7u');
|
const def = ref('iu1Feixah5AeKai3ewooJahjeaegee0eiD4maeth1oul4Hei7u');
|
||||||
|
|
|
@ -1,84 +1,73 @@
|
||||||
import {ref, inject, onBeforeUnmount} from 'vue';
|
import {ref, inject, onBeforeUnmount} from 'vue';
|
||||||
import {router} from '@inertiajs/vue3';
|
import {router} from '@inertiajs/vue3';
|
||||||
|
import type {Ref} from 'vue';
|
||||||
import useQueueEvents from './useQueueEvents.js';
|
import useQueueEvents from './useQueueEvents.js';
|
||||||
|
import { Axios } from 'axios';
|
||||||
|
|
||||||
export function useApiIndex(firstUrl, siteName = null) {
|
export function useApiIndex<D, M extends Custom.PageMetadata>(firstUrl, siteName = null) {
|
||||||
const axios = inject('axios');
|
const axios = inject<Axios>('axios');
|
||||||
|
|
||||||
if (siteName !== null) {
|
if (siteName !== null) {
|
||||||
var {startListener, stopListener} = useQueueEvents(siteName, () => reload());
|
var {startListener, stopListener} = useQueueEvents(siteName, () => reload());
|
||||||
}
|
}
|
||||||
|
|
||||||
const single = ref(null);
|
const single: Ref<D|null> = ref(null);
|
||||||
|
|
||||||
const url = ref(firstUrl);
|
const url = ref(firstUrl);
|
||||||
const inner = {
|
const inner: {data: Ref<D[]|null>, meta: Ref<M|null>} = {
|
||||||
data: ref([]),
|
data: ref(null),
|
||||||
meta: ref({}),
|
meta: ref(null),
|
||||||
};
|
};
|
||||||
|
|
||||||
async function reload(resetPage = true, p = {}) {
|
async function reload(resetPage = true, p = {}) {
|
||||||
var params = {
|
const params = {
|
||||||
page: resetPage ? 1 : inner.meta.value.current_page,
|
page: resetPage ? 1 : inner.meta.value?.current_page,
|
||||||
...p,
|
...p,
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = (await axios.get(url.value, {params})).data;
|
const response = (await axios.get(url.value, {params})).data;
|
||||||
inner.data.value = response.data;
|
inner.data.value = response.data;
|
||||||
inner.meta.value = response.meta;
|
inner.meta.value = response.meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reloadPage(page, p = {}) {
|
async function reloadPage(page: number, p = {}) {
|
||||||
inner.meta.value.current_page = page;
|
if (inner.meta.value?.current_page) {
|
||||||
|
inner.meta.value.current_page = page;
|
||||||
|
}
|
||||||
await reload(false, p);
|
await reload(false, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
function create() {
|
function create() {
|
||||||
single.value = JSON.parse(JSON.stringify(inner.meta.value.default));
|
single.value = JSON.parse(JSON.stringify(inner.meta.value?.default));
|
||||||
}
|
}
|
||||||
|
|
||||||
function edit(model) {
|
function edit(model: D) {
|
||||||
single.value = JSON.parse(JSON.stringify(model));
|
single.value = JSON.parse(JSON.stringify(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
|
if (single.value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
single.value.id ? await axios.patch(single.value.links.update, single.value) : await axios.post(inner.meta.value.links.store, single.value);
|
single.value.id ? await axios.patch(single.value.links.update, single.value) : await axios.post(inner.meta.value.links.store, single.value);
|
||||||
await reload();
|
await reload();
|
||||||
single.value = null;
|
single.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function remove(model) {
|
async function remove(model: D) {
|
||||||
await axios.delete(model.links.destroy);
|
await axios.delete(model.links.destroy);
|
||||||
await reload();
|
await reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function can(permission) {
|
|
||||||
return inner.meta.value.can[permission];
|
|
||||||
}
|
|
||||||
|
|
||||||
function toFilterString(data) {
|
function toFilterString(data) {
|
||||||
return btoa(encodeURIComponent(JSON.stringify(data)));
|
return btoa(encodeURIComponent(JSON.stringify(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestCallback(successMessage, failureMessage) {
|
|
||||||
return {
|
|
||||||
onSuccess: () => {
|
|
||||||
this.$success(successMessage);
|
|
||||||
reload(false);
|
|
||||||
},
|
|
||||||
onFailure: () => {
|
|
||||||
this.$error(failureMessage);
|
|
||||||
reload(false);
|
|
||||||
},
|
|
||||||
preserveState: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
single.value = null;
|
single.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUrl(newUrl) {
|
function updateUrl(newUrl: string) {
|
||||||
url.value = newUrl;
|
url.value = newUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,8 +84,6 @@ export function useApiIndex(firstUrl, siteName = null) {
|
||||||
edit,
|
edit,
|
||||||
reload,
|
reload,
|
||||||
reloadPage,
|
reloadPage,
|
||||||
can,
|
|
||||||
requestCallback,
|
|
||||||
router,
|
router,
|
||||||
submit,
|
submit,
|
||||||
remove,
|
remove,
|
|
@ -8,10 +8,10 @@
|
||||||
</page-header>
|
</page-header>
|
||||||
|
|
||||||
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
||||||
<f-text id="completed_at" v-model="single.completed_at" type="date" label="Datum" required></f-text>
|
<f-text id="completed_at" v-model="single.completed_at" type="date" label="Datum" required />
|
||||||
<f-select id="course_id" v-model="single.course_id" name="course_id" :options="meta.courses" label="Baustein" required></f-select>
|
<f-select id="course_id" v-model="single.course_id" name="course_id" :options="meta.courses" label="Baustein" required />
|
||||||
<f-text id="event_name" v-model="single.event_name" label="Veranstaltung" required></f-text>
|
<f-text id="event_name" v-model="single.event_name" label="Veranstaltung" required />
|
||||||
<f-text id="organizer" v-model="single.organizer" label="Veranstalter" required></f-text>
|
<f-text id="organizer" v-model="single.organizer" label="Veranstalter" required />
|
||||||
<button type="submit" class="btn btn-primary">Absenden</button>
|
<button type="submit" class="btn btn-primary">Absenden</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -23,18 +23,18 @@
|
||||||
<th>Veranstaltung</th>
|
<th>Veranstaltung</th>
|
||||||
<th>Veranstalter</th>
|
<th>Veranstalter</th>
|
||||||
<th>Datum</th>
|
<th>Datum</th>
|
||||||
<th></th>
|
<th />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tr v-for="(course, index) in data" :key="index">
|
<tr v-for="(course, index) in data" :key="index">
|
||||||
<td v-text="course.course_name"></td>
|
<td v-text="course.course_name" />
|
||||||
<td v-text="course.event_name"></td>
|
<td v-text="course.event_name" />
|
||||||
<td v-text="course.organizer"></td>
|
<td v-text="course.organizer" />
|
||||||
<td v-text="course.completed_at_human"></td>
|
<td v-text="course.completed_at_human" />
|
||||||
<td class="flex">
|
<td class="flex">
|
||||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(course)"><ui-sprite src="pencil"></ui-sprite></a>
|
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(course)"><ui-sprite src="pencil" /></a>
|
||||||
<a href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(course)"><ui-sprite src="trash"></ui-sprite></a>
|
<a href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(course)"><ui-sprite src="trash" /></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
<script lang="js" setup>
|
<script lang="js" setup>
|
||||||
defineEmits(['close']);
|
defineEmits(['close']);
|
||||||
import { useApiIndex } from '../../composables/useApiIndex.js';
|
import { useApiIndex } from '../../composables/useApiIndex.ts';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<page-header title="Zahlungen" @close="$emit('close')"> </page-header>
|
<page-header title="Zahlungen" @close="$emit('close')" />
|
||||||
|
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<table class="custom-table custom-table-light custom-table-sm text-sm">
|
<table class="custom-table custom-table-light custom-table-sm text-sm">
|
||||||
|
@ -10,15 +10,15 @@
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tr v-for="(position, index) in data" :key="index">
|
<tr v-for="(position, index) in data" :key="index">
|
||||||
<td v-text="position.description"></td>
|
<td v-text="position.description" />
|
||||||
<td v-text="position.invoice.status"></td>
|
<td v-text="position.invoice.status" />
|
||||||
<td v-text="position.price_human"></td>
|
<td v-text="position.price_human" />
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js" setup>
|
<script lang="ts" setup>
|
||||||
defineEmits(['close']);
|
defineEmits(['close']);
|
||||||
import { useApiIndex } from '../../composables/useApiIndex.js';
|
import { useApiIndex } from '../../composables/useApiIndex.js';
|
||||||
|
|
||||||
|
|
|
@ -6,58 +6,62 @@
|
||||||
</template>
|
</template>
|
||||||
</page-header>
|
</page-header>
|
||||||
|
|
||||||
<form v-if="single" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
<form v-if="single && meta !== null" class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
||||||
<f-select id="group_id" v-model="single.group_id" name="group_id" :options="meta.groups" label="Gruppierung" required />
|
<f-select id="group_id" v-model="single.group" name="group_id" :options="meta.groups" label="Gruppierung" required />
|
||||||
<f-select id="activity_id" v-model="single.activity_id" name="activity_id" :options="meta.activities" label="Tätigkeit" required />
|
<f-select id="activity_id" v-model="single.activity" name="activity_id" :options="meta.activities" label="Tätigkeit" required />
|
||||||
<f-select v-if="single.activity_id"
|
<f-select v-if="single.activity"
|
||||||
id="subactivity_id"
|
id="subactivity_id"
|
||||||
:model-value="single.subactivity_id"
|
:model-value="single.subactivity"
|
||||||
name="subactivity_id"
|
name="subactivity_id"
|
||||||
:options="meta.subactivities[single.activity_id]"
|
:options="meta.subactivities[single.activity.id]"
|
||||||
label="Untertätigkeit"
|
label="Untertätigkeit"
|
||||||
@update:model-value="setSubactivityId(single, $event)"
|
@update:model-value="setSubactivityId(single, $event)"
|
||||||
/>
|
/>
|
||||||
<f-switch v-if="displayPromisedAt"
|
<f-switch v-if="displayPromisedAt"
|
||||||
id="has_promise"
|
id="has_promise"
|
||||||
name="has_promise"
|
name="has_promise"
|
||||||
:model-value="single.promised_at !== null"
|
:model-value="single.promisedAt !== null"
|
||||||
label="Hat Versprechen"
|
label="Hat Versprechen"
|
||||||
@update:model-value="setPromisedAtSwitch(single, $event)"
|
@update:model-value="setPromisedAtSwitch(single, $event)"
|
||||||
/>
|
/>
|
||||||
<f-text v-show="displayPromisedAt && single.promised_at !== null" id="promised_at" v-model="single.promised_at" type="date" label="Versprechensdatum" size="sm" />
|
<f-text v-show="displayPromisedAt && single.promisedAt !== null" id="promised_at" v-model="single.promisedAt" type="date" label="Versprechensdatum" size="sm" />
|
||||||
<button type="submit" class="btn btn-primary">Absenden</button>
|
<button type="submit" class="btn btn-primary">Absenden</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div v-else class="grow">
|
<div v-else class="grow">
|
||||||
<table class="custom-table custom-table-light custom-table-sm text-sm">
|
<table class="custom-table custom-table-light custom-table-sm text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<th>Tätigkeit</th>
|
<tr>
|
||||||
<th>Untertätigkeit</th>
|
<th>Tätigkeit</th>
|
||||||
<th>Datum</th>
|
<th>Untertätigkeit</th>
|
||||||
<th>Aktiv</th>
|
<th>Datum</th>
|
||||||
<th />
|
<th>Aktiv</th>
|
||||||
|
<th />
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tr v-for="(membership, index) in data" :key="index">
|
<tbody>
|
||||||
<td v-text="membership.activity_name" />
|
<tr v-for="(membership, index) in data" :key="index">
|
||||||
<td v-text="membership.subactivity_name" />
|
<td v-text="membership.activity?.name" />
|
||||||
<td v-text="membership.human_date" />
|
<td v-text="membership.subactivity?.name" />
|
||||||
<td><ui-boolean-display :value="membership.is_active" dark /></td>
|
<td v-text="membership.from?.human" />
|
||||||
<td class="flex space-x-1">
|
<td><ui-boolean-display :value="membership.isActive" dark /></td>
|
||||||
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(membership)"><ui-sprite src="pencil" /></a>
|
<td class="flex space-x-1">
|
||||||
<a href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(membership)"><ui-sprite src="trash" /></a>
|
<a href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="edit(membership)"><ui-sprite src="pencil" /></a>
|
||||||
</td>
|
<a href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="remove(membership)"><ui-sprite src="trash" /></a>
|
||||||
</tr>
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
defineEmits(['close']);
|
defineEmits(['close']);
|
||||||
import { useApiIndex } from '../../composables/useApiIndex.js';
|
import { useApiIndex } from '../../composables/useApiIndex.ts';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
url: {
|
url: {
|
||||||
|
@ -65,14 +69,14 @@ const props = defineProps({
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { data, meta, reload, single, create, edit, submit, remove } = useApiIndex(props.url, 'membership');
|
const { data, meta, reload, single, create, edit, submit, remove } = useApiIndex<App.Member.Data.MembershipData, App.Member.Data.MembershipMeta>(props.url, 'membership');
|
||||||
|
|
||||||
function setPromisedAtSwitch(single, value) {
|
function setPromisedAtSwitch(single, value) {
|
||||||
single.promised_at = value ? dayjs().format('YYYY-MM-DD') : null;
|
single.promised_at = value ? dayjs().format('YYYY-MM-DD') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayPromisedAt = computed(function () {
|
const displayPromisedAt = computed(function () {
|
||||||
if (!single.value || !single.value.activity_id || !single.value.subactivity_id) {
|
if (!single.value || !single.value.activity || !single.value.subactivity) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
declare namespace Custom {
|
||||||
|
export type PageMetadata = {
|
||||||
|
current_page: number;
|
||||||
|
default: object;
|
||||||
|
};
|
||||||
|
}
|
|
@ -444,13 +444,20 @@ id: number;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
export type MembershipData = {
|
export type MembershipData = {
|
||||||
id: number;
|
activity: App.Member.Data.ActivityData | null;
|
||||||
activity: App.Member.Data.ActivityData;
|
subactivity: App.Member.Data.SubactivityData | null;
|
||||||
subactivity: App.Member.Data.SubactivityData;
|
group: App.Member.Data.GroupData | null;
|
||||||
group: App.Member.Data.GroupData;
|
promisedAt: App.Lib.Data.DateData | null;
|
||||||
promised_at: App.Lib.Data.DateData | null;
|
from: App.Lib.Data.DateData | null;
|
||||||
from: App.Lib.Data.DateData;
|
isActive: boolean;
|
||||||
is_active: boolean;
|
links: Array<any>;
|
||||||
|
};
|
||||||
|
export type MembershipMeta = {
|
||||||
|
links: Array<any>;
|
||||||
|
groups: any;
|
||||||
|
activities: any;
|
||||||
|
subactivities: any;
|
||||||
|
default: App.Member.Data.MembershipData;
|
||||||
};
|
};
|
||||||
export type NestedGroup = {
|
export type NestedGroup = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
|
@ -37,14 +37,25 @@ it('testItShowsActivityAndSubactivityNamesOfMember', function () {
|
||||||
->assertJsonPath('data.0.subactivity.name', 'Wölfling')
|
->assertJsonPath('data.0.subactivity.name', 'Wölfling')
|
||||||
->assertJsonPath('data.0.from.human', '02.11.2022')
|
->assertJsonPath('data.0.from.human', '02.11.2022')
|
||||||
->assertJsonPath('data.0.from.raw', '2022-11-02')
|
->assertJsonPath('data.0.from.raw', '2022-11-02')
|
||||||
->assertJsonPath('data.0.promised_at.raw', now()->format('Y-m-d'))
|
->assertJsonPath('data.0.promisedAt.raw', now()->format('Y-m-d'))
|
||||||
->assertJsonPath('data.0.promised_at.human', now()->format('d.m.Y'))
|
->assertJsonPath('data.0.promisedAt.human', now()->format('d.m.Y'))
|
||||||
->assertJsonPath('data.0.group.id', $group->id)
|
->assertJsonPath('data.0.group.id', $group->id)
|
||||||
->assertJsonPath('data.0.id', $membership->id)
|
|
||||||
->assertJsonPath('data.0.links.update', route('membership.update', ['membership' => $membership]))
|
->assertJsonPath('data.0.links.update', route('membership.update', ['membership' => $membership]))
|
||||||
->assertJsonPath('data.0.links.destroy', route('membership.destroy', ['membership' => $membership]));
|
->assertJsonPath('data.0.links.destroy', route('membership.destroy', ['membership' => $membership]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('activity and subactivity can be null', function () {
|
||||||
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
|
$group = Group::factory()->create(['name' => 'aaaaaaaa']);
|
||||||
|
$member = Member::factory()
|
||||||
|
->defaults()
|
||||||
|
->for($group)
|
||||||
|
->has(Membership::factory()->for($group)->in('€ Mitglied', 122)->from('2022-11-02')->promise(now()))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$this->get("/member/{$member->id}/membership")->assertNull('data.0.subactivity');
|
||||||
|
});
|
||||||
|
|
||||||
it('returns meta', function () {
|
it('returns meta', function () {
|
||||||
$this->withoutExceptionHandling()->login()->loginNami();
|
$this->withoutExceptionHandling()->login()->loginNami();
|
||||||
$group = Group::factory()->create(['name' => 'aaaaaaaa']);
|
$group = Group::factory()->create(['name' => 'aaaaaaaa']);
|
||||||
|
@ -55,10 +66,11 @@ it('returns meta', function () {
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
$this->get("/member/{$membership->member->id}/membership")
|
$this->get("/member/{$membership->member->id}/membership")
|
||||||
->assertNull('meta.default.activity_id')
|
->assertNull('meta.default.activity')
|
||||||
->assertNull('meta.default.subactivity_id')
|
->assertNull('meta.default.subactivity')
|
||||||
->assertNull('meta.default.promised_at')
|
->assertNull('meta.default.promisedAt')
|
||||||
->assertJsonPath('meta.default.group_id', $group->id)
|
->assertJsonPath('meta.default.group.id', $group->id)
|
||||||
|
->assertJsonPath('meta.default.group.name', $group->name)
|
||||||
->assertJsonPath('meta.groups.0.id', $group->id)
|
->assertJsonPath('meta.groups.0.id', $group->id)
|
||||||
->assertJsonPath('meta.activities.0.id', $membership->activity->id)
|
->assertJsonPath('meta.activities.0.id', $membership->activity->id)
|
||||||
->assertJsonPath('meta.activities.0.name', $membership->activity->name)
|
->assertJsonPath('meta.activities.0.name', $membership->activity->name)
|
||||||
|
@ -76,7 +88,7 @@ it('promised at can be null', function () {
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
$this->get("/member/{$member->id}/membership")
|
$this->get("/member/{$member->id}/membership")
|
||||||
->assertJsonPath('data.0.promised_at', null);
|
->assertJsonPath('data.0.promisedAt', null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +100,7 @@ it('testItShowsIfMembershipIsActive', function (Carbon $from, ?Carbon $to, bool
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
$this->get("/member/{$member->id}/membership")
|
$this->get("/member/{$member->id}/membership")
|
||||||
->assertJsonPath('data.0.is_active', $isActive);
|
->assertJsonPath('data.0.isActive', $isActive);
|
||||||
})->with([
|
})->with([
|
||||||
[now()->subMonths(2), null, true],
|
[now()->subMonths(2), null, true],
|
||||||
[now()->subMonths(2), now()->subDay(), false],
|
[now()->subMonths(2), now()->subDay(), false],
|
||||||
|
|
|
@ -59,5 +59,5 @@
|
||||||
"@/*": ["./resources/js/*"]
|
"@/*": ["./resources/js/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["**/*", "resources/js/components/components.d.ts"]
|
"include": ["**/*", "resources/types/generated.d.ts", "resources/types/custom.d.ts"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue