Add payment management

This commit is contained in:
philipp lang 2021-07-04 16:56:07 +02:00
parent 9c580b7eb5
commit 1dd676d326
22 changed files with 496 additions and 34 deletions

View File

@ -0,0 +1,56 @@
<?php
namespace App\Http\Views;
use App\Member\MemberResource;
use App\Member\Member;
use Illuminate\Http\Request;
use App\Payment\Status;
use App\Payment\Subscription;
use App\Payment\PaymentResource;
class MemberView {
public function index(Request $request) {
return [
'data' => MemberResource::collection(Member::select('*')->search($request->query('search', null))->with('billKind')->withIsConfirmed()->paginate(15)),
'toolbar' => [ ['href' => route('member.index'), 'label' => 'Zurück', 'color' => 'primary', 'icon' => 'plus'] ]
];
}
public function paymentCreate($member) {
return $this->additional($member, [
'model' => [
'subscription_id' => $member->subscription_id,
'status_id' => Status::default(),
'nr' => date('Y'),
],
'links' => [ ['label' => 'Zurück', 'href' => route('member.payment.index', ['member' => $member]) ] ],
'mode' => 'create',
]);
}
public function paymentEdit($member, $payment) {
return $this->additional($member, [
'model' => new PaymentResource($payment),
'links' => [ ['label' => 'Zurück', 'href' => route('member.payment.index', ['member' => $member]) ] ],
'mode' => 'edit',
]);
}
public function paymentIndex($member) {
return $this->additional($member, [
'model' => null,
'links' => [ ['label' => 'Zahlung hinzufügen', 'href' => route('member.payment.create', ['member' => $member]) ] ],
'mode' => 'index',
]);
}
private function additional($member, $overwrites = []) {
return (new MemberResource($member->load('payments')))
->additional(array_merge([
'subscriptions' => Subscription::get()->pluck('name', 'id'),
'statuses' => Status::get()->pluck('name', 'id'),
], $overwrites));
}
}

View File

@ -13,6 +13,7 @@ use App\Activity;
use App\Subactivity;
use Zoomyboy\LaravelNami\NamiUser;
use App\Payment\Subscription;
use App\Payment\Payment;
class Member extends Model
{
@ -87,7 +88,7 @@ class Member extends Model
public function payments()
{
return $this->hasMany(\App\Payment::class)->orderBy('nr');
return $this->hasMany(Payment::class)->orderBy('nr');
}
public function way()

View File

@ -13,6 +13,7 @@ use App\Bill\BillKind;
use App\Activity;
use App\Group;
use App\Payment\Subscription;
use App\Http\Views\MemberView;
class MemberController extends Controller
{
@ -21,10 +22,10 @@ class MemberController extends Controller
session()->put('menu', 'member');
session()->put('title', 'Mitglieder');
return \Inertia::render('member/Index', [
'data' => MemberResource::collection(Member::select('*')->search($request->query('search', null))->with('billKind')->withIsConfirmed()->paginate(15)),
'toolbar' => [ ['href' => route('member.create'), 'label' => 'Mitglied anlegen', 'color' => 'primary', 'icon' => 'plus'] ],
]);
$payload = app(MemberView::class)->index($request);
$payload['toolbar'] = [ ['href' => route('member.create'), 'label' => 'Mitglied anlegen', 'color' => 'primary', 'icon' => 'plus'] ];
return \Inertia::render('member/Index', $payload);
}
public function create() {

View File

@ -3,6 +3,7 @@
namespace App\Member;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Payment\PaymentResource;
class MemberResource extends JsonResource
{
@ -46,6 +47,7 @@ class MemberResource extends JsonResource
'has_nami' => $this->nami_id !== null,
'is_confirmed' => $this->is_confirmed,
'children_phone' => $this->children_phone,
'payments' => PaymentResource::collection($this->whenLoaded('payments')),
];
}
}

28
app/Payment/Payment.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace App\Payment;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Member\Member;
use App\Payment\Status;
use App\Payment\Subscription;
class Payment extends Model
{
use HasFactory;
public $fillable = ['member_id', 'subscription_id', 'nr', 'status_id'];
public function member() {
return $this->belongsTo(Member::class);
}
public function subscription() {
return $this->belongsTo(Subscription::class);
}
public function status() {
return $this->belongsTo(Status::class);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace App\Payment;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Member\Member;
use App\Member\MemberResource;
use App\Http\Views\MemberView;
class PaymentController extends Controller
{
public function index(Request $request, Member $member) {
session()->put('menu', 'member');
session()->put('title', "Zahlungen für Mitglied {$member->fullname}");
$payload = app(MemberView::class)->index($request);
$payload['single'] = app(MemberView::class)->paymentIndex($member);
return \Inertia::render('member/Index', $payload);
}
public function create(Member $member, Request $request) {
session()->put('menu', 'member');
session()->put('title', "Zahlungen für Mitglied {$member->fullname}");
$payload = app(MemberView::class)->index($request);
$payload['single'] = app(MemberView::class)->paymentCreate($member);
return \Inertia::render('member/Index', $payload);
}
public function store(Request $request, Member $member) {
$member->payments()->create($request->validate([
'nr' => 'required|numeric',
'subscription_id' => 'required|exists:subscriptions,id',
'status_id' => 'required|exists:statuses,id',
]));
return redirect()->route('member.payment.index', ['member' => $member]);
}
public function edit(Member $member, Request $request, Payment $payment) {
session()->put('menu', 'member');
session()->put('title', "Zahlungen für Mitglied {$member->fullname}");
$payload = app(MemberView::class)->index($request);
$payload['single'] = app(MemberView::class)->paymentEdit($member, $payment);
return \Inertia::render('member/Index', $payload);
}
public function update(Request $request, Member $member, Payment $payment) {
$payment->update($request->validate([
'nr' => 'required|numeric',
'subscription_id' => 'required|exists:subscriptions,id',
'status_id' => 'required|exists:statuses,id',
]));
return redirect()->route('member.payment.index', ['member' => $member]);
}
public function destroy(Request $request, Member $member, Payment $payment) {
$payment->delete();
return redirect()->route('member.payment.index', ['member' => $member]);
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Payment;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'subscription_id' => $this->subscription_id,
'subscription_name' => $this->subscription->name,
'status_name' => $this->status->name,
'status_id' => $this->status->id,
'nr' => $this->nr,
'id' => $this->id,
];
}
}

18
app/Payment/Status.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Payment;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Status extends Model
{
use HasFactory;
public $fillable = ['name', 'is_bill', 'is_remember'];
public $timestamps = false;
public static function default() {
return static::where('is_bill', true)->where('is_remember', true)->first()->id;
}
}

7
bin/copydb Executable file
View File

@ -0,0 +1,7 @@
#/bin/bash
echo "drop database scoutrobot;" | sudo mysql
echo "create database scoutrobot;" | sudo mysql
ssh -l stammsilva zoomyboy.de "mysqldump -u nami -p$SCOUTROBOT_DB_PASSWORD nami" > db.tmp && sudo mysql scoutrobot < db.tmp
rm db.tmp

View File

@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Payment\Status;
class CreatePaymentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('statuses', function($table) {
$table->id();
$table->string('name');
$table->boolean('is_bill');
$table->boolean('is_remember');
});
Status::create(['name' => 'Nicht bezahlt', 'is_bill' => true, 'is_remember' => true]);
Status::create(['name' => 'Rechnung gestellt', 'is_bill' => false, 'is_remember' => true]);
Status::create(['name' => 'Rechnung beglichen', 'is_bill' => false, 'is_remember' => false]);
Schema::create('payments', function (Blueprint $table) {
$table->id();
$table->string('nr');
$table->foreignId('subscription_id')->constrained();
$table->foreignId('status_id')->constrained();
$table->foreignId('member_id')->constrained();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('payments');
}
}

View File

@ -6,3 +6,4 @@
@import "layout";
@import "buttons";
@import "table";
@import "sidebar";

View File

@ -16,6 +16,12 @@
@apply bg-primary-500;
}
}
&.btn-primary-light {
@apply bg-primary-600 text-primary-800;
&:hover {
@apply bg-primary-500 text-primary-700;
}
}
&.btn-warning {
@apply bg-yellow-700;
&:hover {
@ -34,5 +40,22 @@
@apply bg-red-500;
}
}
&.label {
@apply rounded-full leading-none transition-all normal-case;
&.primary {
@apply bg-primary-800 text-primary-500;
&:hover {
@apply text-primary-400 bg-primary-700;
}
}
}
&.icon {
@apply p-0 flex justify-center items-center w-6 h-6;
svg {
@apply w-3 h-3 text-primary-100 flex-none;
}
}
}

3
resources/css/sidebar.css vendored Normal file
View File

@ -0,0 +1,3 @@
.sidebar {
@apply fixed w-96 shadow-2xl bg-gray-600 right-0 top-0 h-full;
}

View File

@ -1,6 +1,39 @@
.custom-table {
display: table;
width: 100%;
& > header > div {
@apply px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b;
}
& > div {
@apply text-gray-200 transition-all duration-300 rounded hover:bg-gray-800;
& > div {
@apply py-1 px-6;
}
}
&.custom-table-sm {
& > header > div {
@apply px-3 py-2;
}
& > div {
& > div {
@apply py-1 px-3;
}
}
}
&.custom-table-light {
& > header > div {
@apply border-gray-500;
}
& > div {
&:hover {
@apply bg-gray-700;
}
}
}
}
.custom-table > * {
display: table-row;

View File

@ -1 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 20 20" id="check" xmlns="http://www.w3.org/2000/svg"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/></symbol><symbol viewBox="0 0 20 20" id="close" xmlns="http://www.w3.org/2000/svg"><path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/></symbol><symbol viewBox="0 0 512 512" id="loss" xmlns="http://www.w3.org/2000/svg"><path d="M448 281.6h-25.6c-7.68 0-12.8 5.12-12.8 12.8v115.2h51.2V294.4c0-7.68-5.12-12.8-12.8-12.8zM345.6 230.4H320c-7.68 0-12.8 5.12-12.8 12.8v166.4h51.2V243.2c0-7.68-5.12-12.8-12.8-12.8zM243.2 179.2h-25.6c-7.68 0-12.8 5.12-12.8 12.8v217.6H256V192c0-7.68-5.12-12.8-12.8-12.8zM140.8 102.4h-25.6c-7.68 0-12.8 5.12-12.8 12.8v294.4h51.2V115.2c0-7.68-5.12-12.8-12.8-12.8z"/><path d="M486.4 460.8H51.2V25.6C51.2 10.24 40.96 0 25.6 0 10.24 0 0 10.24 0 25.6v460.8C0 501.76 10.24 512 25.6 512h460.8c15.36 0 25.6-10.24 25.6-25.6 0-15.36-10.24-25.6-25.6-25.6z"/></symbol><symbol viewBox="0 0 512 512" id="money" xmlns="http://www.w3.org/2000/svg"><path d="M330.24 94.72l46.08-53.76C391.68 25.6 381.44 0 358.4 0H153.6c-23.04 0-33.28 25.6-20.48 40.96l46.08 53.76C76.8 145.92 0 289.28 0 386.56 0 506.88 115.2 512 256 512s256-5.12 256-125.44c0-97.28-76.8-240.64-181.76-291.84zM256 281.6c10.24 0 17.92 2.56 25.6 5.12 30.72 10.24 51.2 38.4 51.2 71.68 0 33.28-20.48 61.44-51.2 71.68v30.72h-51.2v-30.72c-30.72-10.24-51.2-38.4-51.2-71.68h51.2c0 15.36 10.24 25.6 25.6 25.6s25.6-10.24 25.6-25.6c0-15.36-10.24-25.6-25.6-25.6-10.24 0-17.92-2.56-25.6-5.12-30.72-10.24-51.2-38.4-51.2-71.68 0-33.28 20.48-61.44 51.2-71.68V153.6h51.2v30.72c30.72 10.24 51.2 38.4 51.2 71.68h-51.2c0-15.36-10.24-25.6-25.6-25.6s-25.6 10.24-25.6 25.6 10.24 25.6 25.6 25.6z"/></symbol><symbol viewBox="0 0 512 512" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M51.2 353.28L0 512l158.72-51.2zM87.16 316.492L336.96 66.69l108.61 108.61L195.77 425.102zM504.32 79.36L432.64 7.68c-10.24-10.24-25.6-10.24-35.84 0l-23.04 23.04 107.52 107.52 23.04-23.04c10.24-10.24 10.24-25.6 0-35.84z"/></symbol><symbol viewBox="0 0 512 512" id="user" xmlns="http://www.w3.org/2000/svg"><path d="M0 435.2V512h512v-76.8c0-171.52-512-171.52-512 0z"/><circle cx="256" cy="128" r="128"/></symbol></svg>
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 20 20" id="check" xmlns="http://www.w3.org/2000/svg"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/></symbol><symbol viewBox="0 0 20 20" id="close" xmlns="http://www.w3.org/2000/svg"><path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/></symbol><symbol viewBox="0 0 512 512" id="loss" xmlns="http://www.w3.org/2000/svg"><path d="M448 281.6h-25.6c-7.68 0-12.8 5.12-12.8 12.8v115.2h51.2V294.4c0-7.68-5.12-12.8-12.8-12.8zM345.6 230.4H320c-7.68 0-12.8 5.12-12.8 12.8v166.4h51.2V243.2c0-7.68-5.12-12.8-12.8-12.8zM243.2 179.2h-25.6c-7.68 0-12.8 5.12-12.8 12.8v217.6H256V192c0-7.68-5.12-12.8-12.8-12.8zM140.8 102.4h-25.6c-7.68 0-12.8 5.12-12.8 12.8v294.4h51.2V115.2c0-7.68-5.12-12.8-12.8-12.8z"/><path d="M486.4 460.8H51.2V25.6C51.2 10.24 40.96 0 25.6 0 10.24 0 0 10.24 0 25.6v460.8C0 501.76 10.24 512 25.6 512h460.8c15.36 0 25.6-10.24 25.6-25.6 0-15.36-10.24-25.6-25.6-25.6z"/></symbol><symbol viewBox="0 0 512 512" id="money" xmlns="http://www.w3.org/2000/svg"><path d="M330.24 94.72l46.08-53.76C391.68 25.6 381.44 0 358.4 0H153.6c-23.04 0-33.28 25.6-20.48 40.96l46.08 53.76C76.8 145.92 0 289.28 0 386.56 0 506.88 115.2 512 256 512s256-5.12 256-125.44c0-97.28-76.8-240.64-181.76-291.84zM256 281.6c10.24 0 17.92 2.56 25.6 5.12 30.72 10.24 51.2 38.4 51.2 71.68 0 33.28-20.48 61.44-51.2 71.68v30.72h-51.2v-30.72c-30.72-10.24-51.2-38.4-51.2-71.68h51.2c0 15.36 10.24 25.6 25.6 25.6s25.6-10.24 25.6-25.6c0-15.36-10.24-25.6-25.6-25.6-10.24 0-17.92-2.56-25.6-5.12-30.72-10.24-51.2-38.4-51.2-71.68 0-33.28 20.48-61.44 51.2-71.68V153.6h51.2v30.72c30.72 10.24 51.2 38.4 51.2 71.68h-51.2c0-15.36-10.24-25.6-25.6-25.6s-25.6 10.24-25.6 25.6 10.24 25.6 25.6 25.6z"/></symbol><symbol viewBox="0 0 512 512" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M51.2 353.28L0 512l158.72-51.2zM87.16 316.492L336.96 66.69l108.61 108.61L195.77 425.102zM504.32 79.36L432.64 7.68c-10.24-10.24-25.6-10.24-35.84 0l-23.04 23.04 107.52 107.52 23.04-23.04c10.24-10.24 10.24-25.6 0-35.84z"/></symbol><symbol viewBox="0 0 512 512" id="trash" xmlns="http://www.w3.org/2000/svg"><path d="M486.4 102.4h-128V25.6c0-15.36-10.24-25.6-25.6-25.6H179.2c-15.36 0-25.6 10.24-25.6 25.6v76.8h-128C10.24 102.4 0 112.64 0 128s10.24 25.6 25.6 25.6h460.8c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6zm-179.2 0H204.8V51.2h102.4v51.2zM25.6 204.8l48.64 284.16c2.56 12.8 12.8 23.04 25.6 23.04h312.32c12.8 0 23.04-10.24 25.6-23.04L486.4 204.8H25.6zm128 256c-15.36 0-25.6-10.24-25.6-25.6l-25.6-153.6c0-15.36 10.24-25.6 25.6-25.6s25.6 10.24 25.6 25.6l25.6 153.6c0 15.36-10.24 25.6-25.6 25.6zm128-25.6c0 15.36-10.24 25.6-25.6 25.6s-25.6-10.24-25.6-25.6V281.6c0-15.36 10.24-25.6 25.6-25.6s25.6 10.24 25.6 25.6v153.6zm102.4 0c0 15.36-10.24 25.6-25.6 25.6-15.36 0-25.6-10.24-25.6-25.6l25.6-153.6c0-15.36 10.24-25.6 25.6-25.6s25.6 10.24 25.6 25.6L384 435.2z"/></symbol><symbol viewBox="0 0 512 512" id="user" xmlns="http://www.w3.org/2000/svg"><path d="M0 435.2V512h512v-76.8c0-171.52-512-171.52-512 0z"/><circle cx="256" cy="128" r="128"/></symbol></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,19 @@
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path d="M486.4,102.4h-128V25.6c0-15.36-10.24-25.6-25.6-25.6H179.2c-15.36,0-25.6,10.24-25.6,25.6v76.8h-128
C10.24,102.4,0,112.64,0,128s10.24,25.6,25.6,25.6h460.8c15.36,0,25.6-10.24,25.6-25.6S501.76,102.4,486.4,102.4z M307.2,102.4
H204.8V51.2h102.4V102.4z"/>
</g>
</g>
<g>
<g>
<path d="M25.6,204.8l48.64,284.16c2.56,12.8,12.8,23.04,25.6,23.04h312.32c12.8,0,23.04-10.24,25.6-23.04L486.4,204.8H25.6z
M153.6,460.8c-15.36,0-25.6-10.24-25.6-25.6l-25.6-153.6c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6l25.6,153.6
C179.2,450.56,168.96,460.8,153.6,460.8z M281.6,435.2c0,15.36-10.24,25.6-25.6,25.6s-25.6-10.24-25.6-25.6V281.6
c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6V435.2z M384,435.2c0,15.36-10.24,25.6-25.6,25.6
c-15.36,0-25.6-10.24-25.6-25.6l25.6-153.6c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6L384,435.2z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,27 @@
<template>
<div class="h-16 px-6 flex justify-between items-center border-b border-solid border-gray-500">
<div class="flex items-center">
<span class="text-xl font-semibold text-white" v-html="title"></span>
<a v-for="link in links.filter(link => link.href === undefined)" v-text="link.label" @click.prevent="$emit(link.event)" href="#" class="btn label btn-primary-light icon"></a>
<a v-for="link in links.filter(link => link.href !== undefined)" v-text="link.label" :href="link.href" class="btn label btn-primary-light ml-1"></a>
</div>
<div class="flex ml-4">
<a href="#" @click.prevent="$emit('close')" class="btn label btn-primary-light icon">
<sprite class="w-3 h-3" src="close"></sprite>
</a>
</div>
</div>
</template>
<script>
export default {
props: {
links: {
default: function() { return []; }
},
title: {
default: function() { return ''; }
}
}
};
</script>

View File

@ -13,11 +13,11 @@
</div>
<div class="flex-grow ml-56 bg-gray-900 flex flex-col">
<div class="h-16 p-6 flex justify-between items-center border-b border-gray-600">
<div class="h-16 px-6 flex justify-between items-center border-b border-gray-600">
<div class="flex">
<span class="text-xl font-semibold text-white" v-html="$page.props.title"></span>
<div class="flex ml-4">
<inertia-link v-for="link, index in $page.props.toolbar" :key="index" :href="link.href" v-text="link.label" class="rounded-full leading-none px-3 py-2 text-sm" :class="`bg-${link.color}-800 text-${link.color}-500 hover:text-${link.color}-400 hover:bg-${link.color}-700 transition-all transition-300`">
<inertia-link v-for="link, index in $page.props.toolbar" :key="index" :href="link.href" v-text="link.label" class="btn label" :class="link.color">
<sprite :src="link.icon"></sprite>
</inertia-link>
</div>

View File

@ -1,43 +1,44 @@
<template>
<div>
<div class="custom-table">
<header>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Nachname</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Vorname</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Straße</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">PLZ</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Ort</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Mittendrin</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Nami</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Check</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Rechnung</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Geburtstag</div>
<div class="px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b">Eintritt</div>
<div class="px-6 py-3 border-gray-600 border-b"></div>
<div>Nachname</div>
<div>Vorname</div>
<div>Straße</div>
<div>PLZ</div>
<div>Ort</div>
<div>Mittendrin</div>
<div>Nami</div>
<div>Check</div>
<div>Rechnung</div>
<div>Geburtstag</div>
<div>Eintritt</div>
<div></div>
</header>
<div v-for="member, index in data.data" class="text-gray-200 transition-all duration-300 rounded flex items-center hover:bg-gray-800">
<div class="py-1 px-6" v-text="member.firstname"></div>
<div class="py-1 px-6" v-text="member.lastname"></div>
<div class="py-1 px-6" v-text="`${member.address}`"></div>
<div class="py-1 px-6" v-text="`${member.zip}`"></div>
<div class="py-1 px-6" v-text="`${member.location}`"></div>
<div class="py-1 px-6">
<div v-for="member, index in data.data">
<div v-text="member.firstname"></div>
<div v-text="member.lastname"></div>
<div v-text="`${member.address}`"></div>
<div v-text="`${member.zip}`"></div>
<div v-text="`${member.location}`"></div>
<div>
<v-bool v-model="member.send_newspaper"></v-bool>
</div>
<div class="py-1 px-6">
<div>
<v-bool v-model="member.has_nami"></v-bool>
</div>
<div class="py-1 px-6">
<div>
<v-bool v-model="member.is_confirmed"></v-bool>
</div>
<div class="py-1 px-6">
<div>
<div class="py-1 rounded-full flex text-xs items-center justify-center leading-none bg-primary-900" v-text="member.bill_kind_name" v-if="member.bill_kind_name"></div>
<div class="py-1 rounded-full flex text-xs items-center justify-center leading-none" v-else>Kein</div>
</div>
<div class="py-1 px-6" v-text="`${member.birthday_human}`"></div>
<div class="py-1 px-6" v-text="`${member.joined_at_human}`"></div>
<div class="py-1 px-6 flex">
<div v-text="`${member.birthday_human}`"></div>
<div v-text="`${member.joined_at_human}`"></div>
<div class="flex">
<inertia-link :href="`/member/${member.id}/edit`" class="inline-flex btn btn-warning btn-sm"><sprite src="pencil"></sprite></inertia-link>
<inertia-link :href="`/member/${member.id}/payment`" class="inline-flex btn btn-info btn-sm"><sprite src="money"></sprite></inertia-link>
</div>
@ -48,17 +49,30 @@
<div class="px-6">
<pages class="mt-4" :value="data.meta" :only="['data']"></pages>
</div>
<transition name="sidebar">
<payments v-if="single !== null && single.mode === 'index'" v-model="single"></payments>
<payment-form v-if="single !== null && single.mode === 'create'" v-model="single"></payment-form>
<payment-form v-if="single !== null && single.mode === 'edit'" v-model="single"></payment-form>
</transition>
</div>
</template>
<script>
import App from '../../layouts/App';
import Payments from './Payments.vue';
import PaymentForm from './PaymentForm.vue';
export default {
layout: App,
components: { Payments, PaymentForm },
props:{
data: {}
data: {},
single: {
default: function() { return null; }
},
}
}
</script>

View File

@ -0,0 +1,44 @@
<template>
<div class="sidebar">
<sidebar-header :links="value.links" @close="$inertia.visit('/member')" title="Zahlungen"></sidebar-header>
<form class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
<f-text id="nr" v-model="inner.nr" label="Jahr" required></f-text>
<f-select id="subscription_id" :options="value.subscriptions" v-model="inner.subscription_id" label="Beitrag" required></f-select>
<f-select id="status_id" :options="value.statuses" v-model="inner.status_id" label="Status" required></f-select>
<button type="submit" class="btn btn-primary">Absenden</button>
</form>
</div>
</template>
<script>
import SidebarHeader from '../../components/SidebarHeader.vue';
export default {
data: function() {
return {
inner: {},
};
},
components: { SidebarHeader },
props: {
value: {}
},
methods: {
submit() {
this.value.mode === 'create'
? this.$inertia.post(`/member/${this.value.data.id}/payment`, this.inner)
: this.$inertia.patch(`/member/${this.value.data.id}/payment/${this.inner.id}`, this.inner);
}
},
created() {
this.inner = this.value.model;
}
};
</script>

View File

@ -0,0 +1,42 @@
<template>
<div class="sidebar">
<sidebar-header :links="value.links" @close="$inertia.visit('/member')" title="Zahlungen"></sidebar-header>
<div class="custom-table custom-table-light custom-table-sm text-sm">
<header>
<div>Nr</div>
<div>Status</div>
<div>Beitrag</div>
<div></div>
</header>
<div v-for="payment, index in value.data.payments">
<div v-text="payment.nr"></div>
<div v-text="payment.status_name"></div>
<div v-text="payment.subscription_name"></div>
<div class="flex">
<inertia-link :href="`/member/${value.data.id}/payment/${payment.id}/edit`" class="inline-flex btn btn-warning btn-sm"><sprite src="pencil"></sprite></inertia-link>
<inertia-link href="#" @click.prevent="remove(payment)" class="inline-flex btn btn-danger btn-sm"><sprite src="trash"></sprite></inertia-link>
</div>
</div>
</div>
</div>
</template>
<script>
import SidebarHeader from '../../components/SidebarHeader.vue';
export default {
components: { SidebarHeader },
methods: {
remove(payment) {
this.$inertia.delete(`/member/${this.value.data.id}/payment/${payment.id}`);
}
},
props: {
value: {}
}
};
</script>

View File

@ -1,6 +1,7 @@
<?php
use App\Member\MemberController;
use App\Payment\PaymentController;
use App\Payment\SubscriptionController;
use App\Member\MemberConfirmController;
use App\Http\Controllers\HomeController;
@ -14,6 +15,7 @@ Route::group(['middleware' => 'auth:web'], function () {
Route::get('/', HomeController::class)->name('home');
Route::resource('initialize', InitializeController::class);
Route::resource('member', MemberController::class);
Route::resource('member.payment', PaymentController::class);
Route::resource('subscription', SubscriptionController::class);
Route::post('/member/{member}/confirm', MemberConfirmController::class);
});