diff --git a/app/Http/Views/MemberView.php b/app/Http/Views/MemberView.php new file mode 100644 index 00000000..83c71567 --- /dev/null +++ b/app/Http/Views/MemberView.php @@ -0,0 +1,56 @@ + 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)); + } + +} diff --git a/app/Member/Member.php b/app/Member/Member.php index a4f5707b..74edde17 100644 --- a/app/Member/Member.php +++ b/app/Member/Member.php @@ -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() diff --git a/app/Member/MemberController.php b/app/Member/MemberController.php index 9f6a1e80..a5a312c2 100644 --- a/app/Member/MemberController.php +++ b/app/Member/MemberController.php @@ -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() { diff --git a/app/Member/MemberResource.php b/app/Member/MemberResource.php index 22598e03..acbfd894 100644 --- a/app/Member/MemberResource.php +++ b/app/Member/MemberResource.php @@ -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')), ]; } } diff --git a/app/Payment/Payment.php b/app/Payment/Payment.php new file mode 100644 index 00000000..d58604c3 --- /dev/null +++ b/app/Payment/Payment.php @@ -0,0 +1,28 @@ +belongsTo(Member::class); + } + + public function subscription() { + return $this->belongsTo(Subscription::class); + } + + public function status() { + return $this->belongsTo(Status::class); + } +} diff --git a/app/Payment/PaymentController.php b/app/Payment/PaymentController.php new file mode 100644 index 00000000..2a023f05 --- /dev/null +++ b/app/Payment/PaymentController.php @@ -0,0 +1,68 @@ +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]); + } +} diff --git a/app/Payment/PaymentResource.php b/app/Payment/PaymentResource.php new file mode 100644 index 00000000..711073e8 --- /dev/null +++ b/app/Payment/PaymentResource.php @@ -0,0 +1,26 @@ + $this->subscription_id, + 'subscription_name' => $this->subscription->name, + 'status_name' => $this->status->name, + 'status_id' => $this->status->id, + 'nr' => $this->nr, + 'id' => $this->id, + ]; + } +} diff --git a/app/Payment/Status.php b/app/Payment/Status.php new file mode 100644 index 00000000..d22f70f5 --- /dev/null +++ b/app/Payment/Status.php @@ -0,0 +1,18 @@ +where('is_remember', true)->first()->id; + } +} diff --git a/bin/copydb b/bin/copydb new file mode 100755 index 00000000..2cbb8d93 --- /dev/null +++ b/bin/copydb @@ -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 diff --git a/database/migrations/2021_07_04_101300_create_payments_table.php b/database/migrations/2021_07_04_101300_create_payments_table.php new file mode 100644 index 00000000..750bd808 --- /dev/null +++ b/database/migrations/2021_07_04_101300_create_payments_table.php @@ -0,0 +1,47 @@ +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'); + } +} diff --git a/resources/css/app.css b/resources/css/app.css index 6a848b92..3cdef5ea 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -6,3 +6,4 @@ @import "layout"; @import "buttons"; @import "table"; +@import "sidebar"; diff --git a/resources/css/buttons.css b/resources/css/buttons.css index 911b3857..56151021 100644 --- a/resources/css/buttons.css +++ b/resources/css/buttons.css @@ -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; + } + } } diff --git a/resources/css/sidebar.css b/resources/css/sidebar.css new file mode 100644 index 00000000..1fdc0c11 --- /dev/null +++ b/resources/css/sidebar.css @@ -0,0 +1,3 @@ +.sidebar { + @apply fixed w-96 shadow-2xl bg-gray-600 right-0 top-0 h-full; +} diff --git a/resources/css/table.css b/resources/css/table.css index 3b765669..d3038a9a 100644 --- a/resources/css/table.css +++ b/resources/css/table.css @@ -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; diff --git a/resources/img/sprite.svg b/resources/img/sprite.svg index bcb3dea7..9f3212fb 100644 --- a/resources/img/sprite.svg +++ b/resources/img/sprite.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/resources/img/svg/trash.svg b/resources/img/svg/trash.svg new file mode 100644 index 00000000..4232d55a --- /dev/null +++ b/resources/img/svg/trash.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/resources/js/components/SidebarHeader.vue b/resources/js/components/SidebarHeader.vue new file mode 100644 index 00000000..ff1c15e5 --- /dev/null +++ b/resources/js/components/SidebarHeader.vue @@ -0,0 +1,27 @@ + + + diff --git a/resources/js/layouts/App.vue b/resources/js/layouts/App.vue index a7f88ca1..b324a2ce 100644 --- a/resources/js/layouts/App.vue +++ b/resources/js/layouts/App.vue @@ -13,11 +13,11 @@
-
+
- +
diff --git a/resources/js/views/member/Index.vue b/resources/js/views/member/Index.vue index 74acc6d5..54d4f4c1 100644 --- a/resources/js/views/member/Index.vue +++ b/resources/js/views/member/Index.vue @@ -1,43 +1,44 @@ diff --git a/resources/js/views/member/PaymentForm.vue b/resources/js/views/member/PaymentForm.vue new file mode 100644 index 00000000..0452a8cf --- /dev/null +++ b/resources/js/views/member/PaymentForm.vue @@ -0,0 +1,44 @@ + + + diff --git a/resources/js/views/member/Payments.vue b/resources/js/views/member/Payments.vue new file mode 100644 index 00000000..93279ee0 --- /dev/null +++ b/resources/js/views/member/Payments.vue @@ -0,0 +1,42 @@ + + + diff --git a/routes/web.php b/routes/web.php index a7c66fbb..d39ece45 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ '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); });