aa-kanban 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. aa_kanban-0.5.0/.gitignore +25 -0
  2. aa_kanban-0.5.0/LICENSE +21 -0
  3. aa_kanban-0.5.0/PKG-INFO +96 -0
  4. aa_kanban-0.5.0/README.md +50 -0
  5. aa_kanban-0.5.0/aa_kanban/__init__.py +3 -0
  6. aa_kanban-0.5.0/aa_kanban/admin.py +129 -0
  7. aa_kanban-0.5.0/aa_kanban/apps.py +8 -0
  8. aa_kanban-0.5.0/aa_kanban/auth_hooks.py +33 -0
  9. aa_kanban-0.5.0/aa_kanban/migrations/0001_initial.py +123 -0
  10. aa_kanban-0.5.0/aa_kanban/migrations/0002_kanbangroup_alter_board_view_groups_and_more.py +38 -0
  11. aa_kanban-0.5.0/aa_kanban/migrations/0003_kanbansetting_board_discord_webhook_cards.py +29 -0
  12. aa_kanban-0.5.0/aa_kanban/migrations/__init__.py +1 -0
  13. aa_kanban-0.5.0/aa_kanban/models.py +282 -0
  14. aa_kanban-0.5.0/aa_kanban/permissions.py +77 -0
  15. aa_kanban-0.5.0/aa_kanban/static/aa_kanban/css/kanban.css +50 -0
  16. aa_kanban-0.5.0/aa_kanban/static/aa_kanban/js/kanban.js +144 -0
  17. aa_kanban-0.5.0/aa_kanban/static/aa_kanban/libs/Sortable.min.js +2 -0
  18. aa_kanban-0.5.0/aa_kanban/static/aa_kanban/libs/htmx.min.js +1 -0
  19. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/base.html +14 -0
  20. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/board_detail.html +276 -0
  21. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/index.html +103 -0
  22. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/board_column.html +48 -0
  23. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/board_labels_modal.html +67 -0
  24. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/card_assignees.html +49 -0
  25. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/card_comments.html +24 -0
  26. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/card_item.html +40 -0
  27. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/card_labels.html +57 -0
  28. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/card_modal_content.html +108 -0
  29. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/column_header.html +27 -0
  30. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/create_board_form.html +88 -0
  31. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/edit_board_form.html +96 -0
  32. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/group_list.html +46 -0
  33. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/partials/group_users_modal.html +52 -0
  34. aa_kanban-0.5.0/aa_kanban/templates/aa_kanban/settings.html +68 -0
  35. aa_kanban-0.5.0/aa_kanban/tests/__init__.py +1 -0
  36. aa_kanban-0.5.0/aa_kanban/tests/test_api.py +364 -0
  37. aa_kanban-0.5.0/aa_kanban/tests/test_auth_hooks.py +56 -0
  38. aa_kanban-0.5.0/aa_kanban/tests/test_board_management.py +357 -0
  39. aa_kanban-0.5.0/aa_kanban/tests/test_modal.py +279 -0
  40. aa_kanban-0.5.0/aa_kanban/tests/test_models.py +179 -0
  41. aa_kanban-0.5.0/aa_kanban/tests/test_permissions.py +112 -0
  42. aa_kanban-0.5.0/aa_kanban/tests/test_smoke.py +19 -0
  43. aa_kanban-0.5.0/aa_kanban/tests/test_views.py +216 -0
  44. aa_kanban-0.5.0/aa_kanban/urls.py +90 -0
  45. aa_kanban-0.5.0/aa_kanban/utils.py +26 -0
  46. aa_kanban-0.5.0/aa_kanban/views.py +198 -0
  47. aa_kanban-0.5.0/aa_kanban/views_api.py +502 -0
  48. aa_kanban-0.5.0/aa_kanban/views_settings.py +95 -0
  49. aa_kanban-0.5.0/pyproject.toml +73 -0
@@ -0,0 +1,25 @@
1
+ /build/
2
+ /dist/
3
+ /*.egg-info/
4
+ .idea/
5
+ .tox/
6
+ htmlcov/
7
+ node_modules/
8
+
9
+ *.log
10
+ *.tmp
11
+ *.bak
12
+ *.old
13
+ __pycache__
14
+ .coverage
15
+ /coverage.xml
16
+ alliance_auth.sqlite3
17
+ db.sqlite3
18
+ *.sqlite3
19
+ tmp_env/
20
+ .venv/
21
+
22
+ # Tijdelijke (scratch) scripts in de root
23
+ /*test*.py
24
+ /append_task.py
25
+ /make_migrations.py
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maddog Broekman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.5
2
+ Name: aa-kanban
3
+ Version: 0.5.0
4
+ Summary: Kanban Plugin for Alliance Auth
5
+ Author-email: Maddog Broekman <maddogbroekman@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Maddog Broekman
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Keywords: aa_kanban,allianceauth
29
+ Classifier: Environment :: Web Environment
30
+ Classifier: Framework :: Django
31
+ Classifier: Framework :: Django :: 5.2
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Classifier: Programming Language :: Python
36
+ Classifier: Programming Language :: Python :: 3 :: Only
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Topic :: Internet :: WWW/HTTP
42
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
43
+ Requires-Python: <3.14,>=3.10
44
+ Requires-Dist: allianceauth>=5
45
+ Description-Content-Type: text/markdown
46
+
47
+ # AA Kanban
48
+
49
+ Alliance Auth plugin for native Kanban boards.
50
+
51
+ ## Features
52
+
53
+ - **Native Kanban boards** with an intuitive drag-and-drop interface.
54
+ - **Permission management** per board (read-only and write groups).
55
+ - **Global Discord Webhooks** (Admin panel): Receive notifications when new boards are created.
56
+ - **Board-specific Discord Webhooks** (Admin panel): Receive notifications when cards are moved across lists on a specific board.
57
+ - **Direct Messages (DMs)**: Discord DM notifications are sent directly to users when they are assigned to or removed from cards (requires the `aadiscordbot` app to be installed and active).
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install aa-kanban
63
+ ```
64
+
65
+ Add the app to your `local.py`:
66
+
67
+ ```python
68
+ INSTALLED_APPS += [
69
+ "aa_kanban",
70
+ ]
71
+ ```
72
+
73
+ Run the database migrations:
74
+
75
+ ```bash
76
+ python manage.py migrate
77
+ ```
78
+
79
+ Gather static files:
80
+
81
+ ```bash
82
+ python manage.py collectstatic
83
+ ```
84
+
85
+ Restart your supervisor services to load the new code (e.g., gunicorn and celery).
86
+
87
+ ## Discord Webhooks Configuration
88
+
89
+ To receive updates in your Discord channels, you can configure webhooks in the Django Admin panel:
90
+
91
+ 1. **Global Board Creation Webhook**:
92
+ - Go to **Admin** > **Kanban Settings** > **Global Kanban Settings**.
93
+ - Fill in the **Board creation webhook** field with your Discord Webhook URL. You will receive a message whenever a new board is created.
94
+ 2. **Board-Specific Card Movement Webhooks**:
95
+ - Go to **Admin** > **Boards** and edit a specific board.
96
+ - Fill in the **Discord webhook cards** field. Any card movements between lists on this board will be posted to this webhook.
@@ -0,0 +1,50 @@
1
+ # AA Kanban
2
+
3
+ Alliance Auth plugin for native Kanban boards.
4
+
5
+ ## Features
6
+
7
+ - **Native Kanban boards** with an intuitive drag-and-drop interface.
8
+ - **Permission management** per board (read-only and write groups).
9
+ - **Global Discord Webhooks** (Admin panel): Receive notifications when new boards are created.
10
+ - **Board-specific Discord Webhooks** (Admin panel): Receive notifications when cards are moved across lists on a specific board.
11
+ - **Direct Messages (DMs)**: Discord DM notifications are sent directly to users when they are assigned to or removed from cards (requires the `aadiscordbot` app to be installed and active).
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install aa-kanban
17
+ ```
18
+
19
+ Add the app to your `local.py`:
20
+
21
+ ```python
22
+ INSTALLED_APPS += [
23
+ "aa_kanban",
24
+ ]
25
+ ```
26
+
27
+ Run the database migrations:
28
+
29
+ ```bash
30
+ python manage.py migrate
31
+ ```
32
+
33
+ Gather static files:
34
+
35
+ ```bash
36
+ python manage.py collectstatic
37
+ ```
38
+
39
+ Restart your supervisor services to load the new code (e.g., gunicorn and celery).
40
+
41
+ ## Discord Webhooks Configuration
42
+
43
+ To receive updates in your Discord channels, you can configure webhooks in the Django Admin panel:
44
+
45
+ 1. **Global Board Creation Webhook**:
46
+ - Go to **Admin** > **Kanban Settings** > **Global Kanban Settings**.
47
+ - Fill in the **Board creation webhook** field with your Discord Webhook URL. You will receive a message whenever a new board is created.
48
+ 2. **Board-Specific Card Movement Webhooks**:
49
+ - Go to **Admin** > **Boards** and edit a specific board.
50
+ - Fill in the **Discord webhook cards** field. Any card movements between lists on this board will be posted to this webhook.
@@ -0,0 +1,3 @@
1
+ """Alliance Auth Kanban Plugin."""
2
+
3
+ __version__ = "0.5.0"
@@ -0,0 +1,129 @@
1
+ """Admin configuration for aa_kanban.
2
+
3
+ Business-functionaliteit (boards, lijsten, cards, labels) wordt beheerd via
4
+ de frontend. De admin is hier read-only voor overzicht/debug. Alleen Comments
5
+ blijven bewerkbaar voor moderatie-doeleinden.
6
+ """
7
+
8
+ from django.contrib import admin
9
+
10
+ from .models import Board, Card, Comment, Label, List, KanbanSetting
11
+
12
+
13
+ @admin.register(Board)
14
+ class BoardAdmin(admin.ModelAdmin):
15
+ """Read-only board overview, except for technical webhook settings."""
16
+
17
+ list_display = ("name", "slug", "created_by", "created_at", "updated_at")
18
+ search_fields = ("name", "description")
19
+ readonly_fields = (
20
+ "name",
21
+ "slug",
22
+ "description",
23
+ "view_groups",
24
+ "write_groups",
25
+ "created_by",
26
+ "created_at",
27
+ "updated_at",
28
+ )
29
+
30
+ def has_add_permission(self, request) -> bool: # type: ignore[override]
31
+ return False
32
+
33
+ # Allow changing so admins can edit webhooks
34
+ def has_change_permission(self, request, obj=None) -> bool: # type: ignore[override]
35
+ return True
36
+
37
+ def has_delete_permission(self, request, obj=None) -> bool: # type: ignore[override]
38
+ return False
39
+
40
+ @admin.register(KanbanSetting)
41
+ class KanbanSettingAdmin(admin.ModelAdmin):
42
+ """Global configuration for aa_kanban."""
43
+
44
+ list_display = ("__str__", "board_creation_webhook")
45
+
46
+ def has_add_permission(self, request) -> bool: # type: ignore[override]
47
+ # Only allow 1 instance
48
+ from .models import KanbanSetting
49
+ if KanbanSetting.objects.exists():
50
+ return False
51
+ return True
52
+
53
+
54
+ @admin.register(List)
55
+ class ListAdmin(admin.ModelAdmin):
56
+ """Read-only list overview. Manage columns via the board frontend."""
57
+
58
+ list_display = ("name", "board", "order", "created_at")
59
+ list_filter = ("board",)
60
+ search_fields = ("name", "board__name")
61
+ readonly_fields = ("name", "board", "order", "created_at", "updated_at")
62
+
63
+ def has_add_permission(self, request) -> bool: # type: ignore[override]
64
+ return False
65
+
66
+ def has_change_permission(self, request, obj=None) -> bool: # type: ignore[override]
67
+ return False
68
+
69
+ def has_delete_permission(self, request, obj=None) -> bool: # type: ignore[override]
70
+ return False
71
+
72
+
73
+ @admin.register(Label)
74
+ class LabelAdmin(admin.ModelAdmin):
75
+ """Read-only label overview. Manage labels via the board frontend."""
76
+
77
+ list_display = ("name", "color", "board")
78
+ list_filter = ("board", "color")
79
+ search_fields = ("name", "board__name")
80
+ readonly_fields = ("name", "color", "board")
81
+
82
+ def has_add_permission(self, request) -> bool: # type: ignore[override]
83
+ return False
84
+
85
+ def has_change_permission(self, request, obj=None) -> bool: # type: ignore[override]
86
+ return False
87
+
88
+ def has_delete_permission(self, request, obj=None) -> bool: # type: ignore[override]
89
+ return False
90
+
91
+
92
+ @admin.register(Card)
93
+ class CardAdmin(admin.ModelAdmin):
94
+ """Read-only card overview. Manage cards via the board frontend."""
95
+
96
+ list_display = ("title", "list", "order", "due_date", "created_at")
97
+ list_filter = ("list__board", "list")
98
+ search_fields = ("title", "description")
99
+ readonly_fields = (
100
+ "title",
101
+ "description",
102
+ "list",
103
+ "order",
104
+ "assignees",
105
+ "labels",
106
+ "due_date",
107
+ "created_by",
108
+ "created_at",
109
+ "updated_at",
110
+ )
111
+
112
+ def has_add_permission(self, request) -> bool: # type: ignore[override]
113
+ return False
114
+
115
+ def has_change_permission(self, request, obj=None) -> bool: # type: ignore[override]
116
+ return False
117
+
118
+ def has_delete_permission(self, request, obj=None) -> bool: # type: ignore[override]
119
+ return False
120
+
121
+
122
+ @admin.register(Comment)
123
+ class CommentAdmin(admin.ModelAdmin):
124
+ """Comments are manageable from admin for moderation purposes."""
125
+
126
+ list_display = ("card", "author", "created_at")
127
+ list_filter = ("created_at",)
128
+ search_fields = ("text", "author__username", "card__title")
129
+ readonly_fields = ("created_at",)
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AaKanbanConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "aa_kanban"
7
+ label = "aa_kanban"
8
+ verbose_name = "Kanban"
@@ -0,0 +1,33 @@
1
+ """Alliance Auth hooks for aa_kanban."""
2
+
3
+ from allianceauth import hooks
4
+ from allianceauth.services.hooks import MenuItemHook, UrlHook
5
+
6
+ from . import urls
7
+
8
+
9
+ class AaKanbanMenuItem(MenuItemHook):
10
+ """Menu item hook for aa_kanban."""
11
+
12
+ def __init__(self):
13
+ super().__init__(
14
+ "Kanban",
15
+ "fas fa-columns fa-fw",
16
+ "aa_kanban:index",
17
+ navactive=["aa_kanban:"],
18
+ )
19
+
20
+ def render(self, request):
21
+ if request.user.has_perm("aa_kanban.basic_access"):
22
+ return super().render(request)
23
+ return ""
24
+
25
+
26
+ @hooks.register("menu_item_hook")
27
+ def register_menu():
28
+ return AaKanbanMenuItem()
29
+
30
+
31
+ @hooks.register("url_hook")
32
+ def register_urls():
33
+ return UrlHook(urls, "aa_kanban", r"^kanban/")
@@ -0,0 +1,123 @@
1
+ # Generated by Django 5.2.17 on 2026-09-09 20:28
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ initial = True
11
+
12
+ dependencies = [
13
+ ('auth', '0012_alter_user_first_name_max_length'),
14
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.CreateModel(
19
+ name='General',
20
+ fields=[
21
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22
+ ],
23
+ options={
24
+ 'permissions': (('basic_access', 'Can access this app'), ('manage_boards', 'Can manage kanban boards')),
25
+ 'managed': False,
26
+ 'default_permissions': (),
27
+ },
28
+ ),
29
+ migrations.CreateModel(
30
+ name='Board',
31
+ fields=[
32
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
33
+ ('name', models.CharField(max_length=255)),
34
+ ('slug', models.SlugField(blank=True, max_length=255, unique=True)),
35
+ ('description', models.TextField(blank=True)),
36
+ ('created_at', models.DateTimeField(auto_now_add=True)),
37
+ ('updated_at', models.DateTimeField(auto_now=True)),
38
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
39
+ ('view_groups', models.ManyToManyField(blank=True, help_text='Groups that have read-only access to this board.', related_name='kanban_view_boards', to='auth.group')),
40
+ ('write_groups', models.ManyToManyField(blank=True, help_text='Groups that have write/mutation access to this board.', related_name='kanban_write_boards', to='auth.group')),
41
+ ],
42
+ options={
43
+ 'verbose_name': 'Board',
44
+ 'verbose_name_plural': 'Boards',
45
+ 'ordering': ['name'],
46
+ },
47
+ ),
48
+ migrations.CreateModel(
49
+ name='Card',
50
+ fields=[
51
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
52
+ ('title', models.CharField(max_length=255)),
53
+ ('description', models.TextField(blank=True)),
54
+ ('order', models.PositiveIntegerField(default=0)),
55
+ ('due_date', models.DateTimeField(blank=True, null=True)),
56
+ ('created_at', models.DateTimeField(auto_now_add=True)),
57
+ ('updated_at', models.DateTimeField(auto_now=True)),
58
+ ('assignees', models.ManyToManyField(blank=True, related_name='assigned_kanban_cards', to=settings.AUTH_USER_MODEL)),
59
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
60
+ ],
61
+ options={
62
+ 'verbose_name': 'Card',
63
+ 'verbose_name_plural': 'Cards',
64
+ 'ordering': ['order', 'id'],
65
+ },
66
+ ),
67
+ migrations.CreateModel(
68
+ name='Comment',
69
+ fields=[
70
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
71
+ ('text', models.TextField()),
72
+ ('created_at', models.DateTimeField(auto_now_add=True)),
73
+ ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='kanban_comments', to=settings.AUTH_USER_MODEL)),
74
+ ('card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='aa_kanban.card')),
75
+ ],
76
+ options={
77
+ 'verbose_name': 'Comment',
78
+ 'verbose_name_plural': 'Comments',
79
+ 'ordering': ['created_at'],
80
+ },
81
+ ),
82
+ migrations.CreateModel(
83
+ name='Label',
84
+ fields=[
85
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
86
+ ('name', models.CharField(max_length=50)),
87
+ ('color', models.CharField(default='primary', help_text='Bootstrap badge color style (e.g. primary, success, danger, warning, info, dark)', max_length=20)),
88
+ ('board', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='labels', to='aa_kanban.board')),
89
+ ],
90
+ options={
91
+ 'verbose_name': 'Label',
92
+ 'verbose_name_plural': 'Labels',
93
+ 'ordering': ['name'],
94
+ 'unique_together': {('board', 'name')},
95
+ },
96
+ ),
97
+ migrations.AddField(
98
+ model_name='card',
99
+ name='labels',
100
+ field=models.ManyToManyField(blank=True, related_name='cards', to='aa_kanban.label'),
101
+ ),
102
+ migrations.CreateModel(
103
+ name='List',
104
+ fields=[
105
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
106
+ ('name', models.CharField(max_length=100)),
107
+ ('order', models.PositiveIntegerField(default=0)),
108
+ ('created_at', models.DateTimeField(auto_now_add=True)),
109
+ ('updated_at', models.DateTimeField(auto_now=True)),
110
+ ('board', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lists', to='aa_kanban.board')),
111
+ ],
112
+ options={
113
+ 'verbose_name': 'List',
114
+ 'verbose_name_plural': 'Lists',
115
+ 'ordering': ['order', 'id'],
116
+ },
117
+ ),
118
+ migrations.AddField(
119
+ model_name='card',
120
+ name='list',
121
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cards', to='aa_kanban.list'),
122
+ ),
123
+ ]
@@ -0,0 +1,38 @@
1
+ # Generated by Django 5.2.17 on 2026-09-21 08:37
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('aa_kanban', '0001_initial'),
11
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='KanbanGroup',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('name', models.CharField(max_length=255, unique=True)),
20
+ ('members', models.ManyToManyField(blank=True, related_name='kanban_groups', to=settings.AUTH_USER_MODEL)),
21
+ ],
22
+ options={
23
+ 'verbose_name': 'Kanban Group',
24
+ 'verbose_name_plural': 'Kanban Groups',
25
+ 'ordering': ['name'],
26
+ },
27
+ ),
28
+ migrations.AlterField(
29
+ model_name='board',
30
+ name='view_groups',
31
+ field=models.ManyToManyField(blank=True, help_text='Kanban groups that have read-only access to this board.', related_name='kanban_view_boards', to='aa_kanban.kanbangroup'),
32
+ ),
33
+ migrations.AlterField(
34
+ model_name='board',
35
+ name='write_groups',
36
+ field=models.ManyToManyField(blank=True, help_text='Kanban groups that have write/mutation access to this board.', related_name='kanban_write_boards', to='aa_kanban.kanbangroup'),
37
+ ),
38
+ ]
@@ -0,0 +1,29 @@
1
+ # Generated by Django 5.2.17 on 2026-09-21 09:53
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('aa_kanban', '0002_kanbangroup_alter_board_view_groups_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.CreateModel(
14
+ name='KanbanSetting',
15
+ fields=[
16
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17
+ ('board_creation_webhook', models.URLField(blank=True, help_text='Discord Webhook URL for global board creation notifications.', null=True)),
18
+ ],
19
+ options={
20
+ 'verbose_name': 'Kanban Setting',
21
+ 'verbose_name_plural': 'Kanban Settings',
22
+ },
23
+ ),
24
+ migrations.AddField(
25
+ model_name='board',
26
+ name='discord_webhook_cards',
27
+ field=models.URLField(blank=True, help_text='Discord Webhook URL for card movement notifications on this board.', null=True),
28
+ ),
29
+ ]
@@ -0,0 +1 @@
1
+ """Database migrations package."""