diff --git a/app/Activity.php b/app/Activity.php
index b0dd3703..f469bb10 100644
--- a/app/Activity.php
+++ b/app/Activity.php
@@ -2,13 +2,15 @@
namespace App;
+use App\Nami\HasNamiField;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Activity extends Model
{
+ use HasNamiField;
+
public $fillable = ['name', 'nami_id'];
public $timestamps = false;
@@ -20,7 +22,4 @@ class Activity extends Model
return $this->belongsToMany(Subactivity::class);
}
- public static function nami(int $id): ?self {
- return static::firstWhere('nami_id', $id);
- }
}
diff --git a/app/Http/Views/MemberView.php b/app/Http/Views/MemberView.php
index ac4817e0..bc4be28a 100644
--- a/app/Http/Views/MemberView.php
+++ b/app/Http/Views/MemberView.php
@@ -13,7 +13,13 @@ use Illuminate\Http\Request;
class MemberView {
public function index(Request $request, array $filter) {
return [
- 'data' => MemberResource::collection(Member::select('*')->filter($filter)->search($request->query('search', null))->with('billKind')->with('payments')->withSubscriptionName()->withIsConfirmed()->withPendingPayment()->orderByRaw('lastname, firstname')->paginate(15)),
+ 'data' => MemberResource::collection(Member::select('*')
+ ->filter($filter)->search($request->query('search', null))
+ ->with('billKind')->with('payments')
+ ->withSubscriptionName()->withIsConfirmed()->withPendingPayment()->withAgeGroup()
+ ->orderByRaw('lastname, firstname')
+ ->paginate(15)
+ ),
'toolbar' => [ ['href' => route('member.index'), 'label' => 'Zurück', 'color' => 'primary', 'icon' => 'plus'] ],
'paymentDefaults' => ['nr' => date('Y')],
'subscriptions' => Subscription::get()->pluck('name', 'id'),
diff --git a/app/Member/Member.php b/app/Member/Member.php
index 16d48da0..eac57b19 100644
--- a/app/Member/Member.php
+++ b/app/Member/Member.php
@@ -160,6 +160,16 @@ class Member extends Model
]);
}
+ public function scopeWithAgeGroup(Builder $q): Builder {
+ return $q->addSelect([
+ 'age_group_icon' => Subactivity::select('slug')
+ ->join('memberships', 'memberships.subactivity_id', 'subactivities.id')
+ ->where('subactivities.is_age_group', true)
+ ->whereColumn('memberships.member_id', 'members.id')
+ ->limit(1)
+ ]);
+ }
+
public function scopeWhereHasPendingPayment(Builder $q): Builder {
return $q->whereHas('payments', function(Builder $q): void {
$q->whereNeedsPayment();
diff --git a/app/Member/MemberResource.php b/app/Member/MemberResource.php
index 64255015..36ab2cd9 100644
--- a/app/Member/MemberResource.php
+++ b/app/Member/MemberResource.php
@@ -52,6 +52,7 @@ class MemberResource extends JsonResource
'pending_payment' => $this->pending_payment ? number_format($this->pending_payment / 100, 2, ',', '.').' €' : null,
'first_activity_id' => $this->first_activity_id,
'first_subactivity_id' => $this->first_subactivity_id,
+ 'age_group_icon' => $this->age_group_icon,
];
}
}
diff --git a/app/Member/Membership.php b/app/Member/Membership.php
index 7ffb598e..339667dd 100644
--- a/app/Member/Membership.php
+++ b/app/Member/Membership.php
@@ -9,5 +9,5 @@ class Membership extends Model
{
use HasFactory;
- public $fillable = ['activity_id', 'group_id', 'member_id', 'nami_id', 'created_at'];
+ public $fillable = ['subactivity_id', 'activity_id', 'group_id', 'member_id', 'nami_id', 'created_at'];
}
diff --git a/app/Nami/HasNamiField.php b/app/Nami/HasNamiField.php
new file mode 100644
index 00000000..7a1c79ce
--- /dev/null
+++ b/app/Nami/HasNamiField.php
@@ -0,0 +1,21 @@
+ 'boolean',
+ ];
+
+ public function sluggable(): array
+ {
+ return [
+ 'slug' => [
+ 'source' => 'name',
+ ],
+ ];
+ }
+
+ public function activities(): BelongsToMany
+ {
return $this->belongsToMany(Activity::class);
}
+
}
diff --git a/composer.json b/composer.json
index bf7023b9..a1bccfca 100644
--- a/composer.json
+++ b/composer.json
@@ -14,6 +14,7 @@
"require": {
"php": "^7.2.5",
"aweos/agnoster-installer": "1.0",
+ "cviebrock/eloquent-sluggable": "^8.0",
"doctrine/dbal": "^3.1",
"fideloper/proxy": "^4.2",
"fruitcake/laravel-cors": "^1.0",
diff --git a/composer.lock b/composer.lock
index 86ca0e5a..1db14515 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "308f33b72c09fca3d6d3153bfb3cab1e",
+ "content-hash": "edaca62d8f09ce92797f46e31f41b45a",
"packages": [
{
"name": "asm89/stack-cors",
@@ -147,6 +147,80 @@
],
"time": "2021-01-20T22:51:39+00:00"
},
+ {
+ "name": "cocur/slugify",
+ "version": "v4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cocur/slugify.git",
+ "reference": "3f1ffc300f164f23abe8b64ffb3f92d35cec8307"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cocur/slugify/zipball/3f1ffc300f164f23abe8b64ffb3f92d35cec8307",
+ "reference": "3f1ffc300f164f23abe8b64ffb3f92d35cec8307",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=7.0"
+ },
+ "conflict": {
+ "symfony/config": "<3.4 || >=4,<4.3",
+ "symfony/dependency-injection": "<3.4 || >=4,<4.3",
+ "symfony/http-kernel": "<3.4 || >=4,<4.3",
+ "twig/twig": "<2.12.1"
+ },
+ "require-dev": {
+ "laravel/framework": "~5.1",
+ "latte/latte": "~2.2",
+ "league/container": "^2.2.0",
+ "mikey179/vfsstream": "~1.6.8",
+ "mockery/mockery": "^1.3",
+ "nette/di": "~2.4",
+ "phpunit/phpunit": "^5.7.27",
+ "pimple/pimple": "~1.1",
+ "plumphp/plum": "~0.1",
+ "symfony/config": "^3.4 || ^4.3 || ^5.0",
+ "symfony/dependency-injection": "^3.4 || ^4.3 || ^5.0",
+ "symfony/http-kernel": "^3.4 || ^4.3 || ^5.0",
+ "twig/twig": "^2.12.1 || ~3.0",
+ "zendframework/zend-modulemanager": "~2.2",
+ "zendframework/zend-servicemanager": "~2.2",
+ "zendframework/zend-view": "~2.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Cocur\\Slugify\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Florian Eckerstorfer",
+ "email": "florian@eckerstorfer.co",
+ "homepage": "https://florian.ec"
+ },
+ {
+ "name": "Ivo Bathke",
+ "email": "ivo.bathke@gmail.com"
+ }
+ ],
+ "description": "Converts a string into a slug.",
+ "keywords": [
+ "slug",
+ "slugify"
+ ],
+ "support": {
+ "issues": "https://github.com/cocur/slugify/issues",
+ "source": "https://github.com/cocur/slugify/tree/master"
+ },
+ "time": "2019-12-14T13:04:14+00:00"
+ },
{
"name": "composer/package-versions-deprecated",
"version": "1.11.99.2",
@@ -220,6 +294,79 @@
],
"time": "2021-05-24T07:46:03+00:00"
},
+ {
+ "name": "cviebrock/eloquent-sluggable",
+ "version": "8.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cviebrock/eloquent-sluggable.git",
+ "reference": "16e21db24d80180f870c3c7c4faf3d3af23f4117"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cviebrock/eloquent-sluggable/zipball/16e21db24d80180f870c3c7c4faf3d3af23f4117",
+ "reference": "16e21db24d80180f870c3c7c4faf3d3af23f4117",
+ "shasum": ""
+ },
+ "require": {
+ "cocur/slugify": "^4.0",
+ "illuminate/config": "^8.0",
+ "illuminate/database": "^8.0",
+ "illuminate/support": "^8.0",
+ "php": "^7.3|^8.0"
+ },
+ "require-dev": {
+ "limedeck/phpunit-detailed-printer": "^6.0",
+ "mockery/mockery": "^1.4.2",
+ "orchestra/database": "^6.0",
+ "orchestra/testbench": "^6.0",
+ "phpunit/phpunit": "^9.4"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Cviebrock\\EloquentSluggable\\ServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Cviebrock\\EloquentSluggable\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Colin Viebrock",
+ "email": "colin@viebrock.ca"
+ }
+ ],
+ "description": "Easy creation of slugs for your Eloquent models in Laravel",
+ "homepage": "https://github.com/cviebrock/eloquent-sluggable",
+ "keywords": [
+ "eloquent",
+ "eloquent-sluggable",
+ "laravel",
+ "lumen",
+ "slug",
+ "sluggable"
+ ],
+ "support": {
+ "issues": "https://github.com/cviebrock/eloquent-sluggable/issues",
+ "source": "https://github.com/cviebrock/eloquent-sluggable/tree/8.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/cviebrock",
+ "type": "github"
+ }
+ ],
+ "time": "2021-06-12T01:05:33+00:00"
+ },
{
"name": "doctrine/cache",
"version": "2.0.3",
diff --git a/database/migrations/2021_08_22_151457_create_memberships_subactivity_id_column.php b/database/migrations/2021_08_22_151457_create_memberships_subactivity_id_column.php
new file mode 100644
index 00000000..0cfceb05
--- /dev/null
+++ b/database/migrations/2021_08_22_151457_create_memberships_subactivity_id_column.php
@@ -0,0 +1,59 @@
+foreignId('subactivity_id')->nullable()->constrained();
+ $table->unsignedBigInteger('activity_id')->change();
+ $table->foreign('activity_id')->references('id')->on('activities');
+ });
+
+ Member::whereNotNull('nami_id')->get()->each(function($member): void {
+ collect($member->getNamiMemberships(Nami::login(env('NAMI_ADMIN_USER'), env('NAMI_ADMIN_PW'))))->filter(
+ fn ($membership): bool => dump($membership) && $membership['ends_at'] === null,
+ )->each(function($membership) use ($member): void {
+ if ($member->memberships()->where('nami_id', $membership['id'])->exists()) {
+ return;
+ }
+
+ $member->memberships()->create([
+ 'nami_id' => $membership['id'],
+ 'activity_id' => Activity::nami($membership['activity_id'])->id,
+ 'subactivity_id' => $membership['subactivity_id']
+ ? Subactivity::nami($membership['subactivity_id'])->id
+ : null,
+ 'group_id' => Group::nami($membership['group_id'])->id,
+ 'created_at' => $membership['starts_at'],
+ ]);
+ });
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('memberships', function (Blueprint $table) {
+ //
+ });
+ }
+}
diff --git a/database/migrations/2021_08_22_165326_create_activities_slug_column.php b/database/migrations/2021_08_22_165326_create_activities_slug_column.php
new file mode 100644
index 00000000..814c1541
--- /dev/null
+++ b/database/migrations/2021_08_22_165326_create_activities_slug_column.php
@@ -0,0 +1,36 @@
+boolean('is_age_group')->default(false)->after('name');
+ $table->string('slug')->after('name');
+ });
+ Subactivity::get()->each(fn ($subactivity) => $subactivity->update([]));
+ Subactivity::whereIn('nami_id', [1,2,3,4,49])->get()->each(fn ($subactivity) => $subactivity->update(['is_age_group' => true]));
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('activities', function (Blueprint $table) {
+ $table->dropColumn('slug');
+ });
+ }
+}
diff --git a/resources/css/table.css b/resources/css/table.css
index d3038a9a..518bb1bd 100644
--- a/resources/css/table.css
+++ b/resources/css/table.css
@@ -1,33 +1,32 @@
.custom-table {
- display: table;
width: 100%;
- & > header > div {
- @apply px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b;
+ & > thead > th {
+ @apply text-left px-6 text-gray-200 font-semibold py-3 border-gray-600 border-b;
}
- & > div {
+ & > tr {
@apply text-gray-200 transition-all duration-300 rounded hover:bg-gray-800;
- & > div {
+ & > td {
@apply py-1 px-6;
}
}
&.custom-table-sm {
- & > header > div {
+ & > thead > th {
@apply px-3 py-2;
}
- & > div {
- & > div {
+ & > tr {
+ & > td {
@apply py-1 px-3;
}
}
}
&.custom-table-light {
- & > header > div {
+ & > thead > th {
@apply border-gray-500;
}
- & > div {
+ & > td {
&:hover {
@apply bg-gray-700;
}
diff --git a/resources/img/svg/lilie.svg b/resources/img/svg/lilie.svg
new file mode 100644
index 00000000..9b10736a
--- /dev/null
+++ b/resources/img/svg/lilie.svg
@@ -0,0 +1 @@
+
diff --git a/resources/js/views/member/Index.vue b/resources/js/views/member/Index.vue
index b82ae200..77f365e3 100644
--- a/resources/js/views/member/Index.vue
+++ b/resources/js/views/member/Index.vue
@@ -3,57 +3,61 @@
+ | Nachname | +Vorname | +Straße | +PLZ | +Ort | +Tags | +Beitrag | +Geburtstag | +Rechnung | +Ausstand | +Eintritt | ++ - |
---|