several fixes, incl. location of templates/apps/epic/tarot_deck.html to apps/gameboard/tarot_deck.html; added this convention to CLAUDE.md; Game Kit applet items now plentiful enough to bother w. text wrapping in _gameboard.scss; unlocked_decks differentiates from equipped_deck in apps.lyric.models; new migrations accordingly; apps.gameboard.views accounts for only unlocked_decks in deck_variants now; apps.epic.views redirected to new tarot_deck.html location
This commit is contained in:
@@ -73,6 +73,14 @@ src/
|
|||||||
functional_tests/
|
functional_tests/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Template directory convention
|
||||||
|
Templates live under `templates/apps/<frontend-app>/`, not under the backend app that owns the view logic. Specifically:
|
||||||
|
- `lyric/` views → `templates/apps/dashboard/`
|
||||||
|
- `epic/` views → `templates/apps/gameboard/`
|
||||||
|
- `drama/` views → `templates/apps/billboard/`
|
||||||
|
|
||||||
|
Backend apps (`lyric`, `epic`, `drama`) have **no** `templates/` subdirectory.
|
||||||
|
|
||||||
## Dev Commands
|
## Dev Commands
|
||||||
```bash
|
```bash
|
||||||
# Dev server (ASGI — required for WebSockets; no npm/webpack build step)
|
# Dev server (ASGI — required for WebSockets; no npm/webpack build step)
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ def tarot_deck(request, room_id):
|
|||||||
room=room,
|
room=room,
|
||||||
defaults={"deck_variant": deck_variant},
|
defaults={"deck_variant": deck_variant},
|
||||||
)
|
)
|
||||||
return render(request, "apps/epic/tarot_deck.html", {
|
return render(request, "apps/gameboard/tarot_deck.html", {
|
||||||
"room": room,
|
"room": room,
|
||||||
"deck": deck,
|
"deck": deck,
|
||||||
"remaining": deck.remaining_count,
|
"remaining": deck.remaining_count,
|
||||||
@@ -499,7 +499,7 @@ def tarot_deal(request, room_id):
|
|||||||
}
|
}
|
||||||
for i, (card, is_reversed) in enumerate(drawn)
|
for i, (card, is_reversed) in enumerate(drawn)
|
||||||
]
|
]
|
||||||
return render(request, "apps/epic/tarot_deck.html", {
|
return render(request, "apps/gameboard/tarot_deck.html", {
|
||||||
"room": room,
|
"room": room,
|
||||||
"deck": deck,
|
"deck": deck,
|
||||||
"remaining": deck.remaining_count,
|
"remaining": deck.remaining_count,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def gameboard(request):
|
|||||||
"carte": carte,
|
"carte": carte,
|
||||||
"equipped_trinket_id": str(request.user.equipped_trinket_id or ""),
|
"equipped_trinket_id": str(request.user.equipped_trinket_id or ""),
|
||||||
"equipped_deck_id": str(request.user.equipped_deck_id or ""),
|
"equipped_deck_id": str(request.user.equipped_deck_id or ""),
|
||||||
"deck_variants": list(DeckVariant.objects.all()),
|
"deck_variants": list(request.user.unlocked_decks.all()),
|
||||||
"free_tokens": free_tokens,
|
"free_tokens": free_tokens,
|
||||||
"free_count": len(free_tokens),
|
"free_count": len(free_tokens),
|
||||||
"applets": applet_context(request.user, "gameboard"),
|
"applets": applet_context(request.user, "gameboard"),
|
||||||
@@ -62,7 +62,7 @@ def toggle_game_applets(request):
|
|||||||
"carte": request.user.tokens.filter(token_type=Token.CARTE).first(),
|
"carte": request.user.tokens.filter(token_type=Token.CARTE).first(),
|
||||||
"equipped_trinket_id": str(request.user.equipped_trinket_id or ""),
|
"equipped_trinket_id": str(request.user.equipped_trinket_id or ""),
|
||||||
"equipped_deck_id": str(request.user.equipped_deck_id or ""),
|
"equipped_deck_id": str(request.user.equipped_deck_id or ""),
|
||||||
"deck_variants": list(DeckVariant.objects.all()),
|
"deck_variants": list(request.user.unlocked_decks.all()),
|
||||||
"free_tokens": list(request.user.tokens.filter(
|
"free_tokens": list(request.user.tokens.filter(
|
||||||
token_type=Token.FREE, expires_at__gt=timezone.now()
|
token_type=Token.FREE, expires_at__gt=timezone.now()
|
||||||
).order_by("expires_at")),
|
).order_by("expires_at")),
|
||||||
|
|||||||
19
src/apps/lyric/migrations/0015_user_unlocked_decks.py
Normal file
19
src/apps/lyric/migrations/0015_user_unlocked_decks.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 6.0 on 2026-03-25 02:08
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0010_seed_deck_variants_and_earthman'),
|
||||||
|
('lyric', '0014_user_equipped_deck'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='unlocked_decks',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='unlocked_by', to='epic.deckvariant'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -41,6 +41,9 @@ class User(AbstractBaseUser):
|
|||||||
"epic.DeckVariant", null=True, blank=True,
|
"epic.DeckVariant", null=True, blank=True,
|
||||||
on_delete=models.SET_NULL, related_name="+",
|
on_delete=models.SET_NULL, related_name="+",
|
||||||
)
|
)
|
||||||
|
unlocked_decks = models.ManyToManyField(
|
||||||
|
"epic.DeckVariant", blank=True, related_name="unlocked_by",
|
||||||
|
)
|
||||||
|
|
||||||
is_staff = models.BooleanField(default=False)
|
is_staff = models.BooleanField(default=False)
|
||||||
is_superuser = models.BooleanField(default=False)
|
is_superuser = models.BooleanField(default=False)
|
||||||
@@ -175,3 +178,5 @@ def create_wallet_and_tokens(sender, instance, created, **kwargs):
|
|||||||
earthman = DeckVariant.objects.filter(slug="earthman").first()
|
earthman = DeckVariant.objects.filter(slug="earthman").first()
|
||||||
instance.equipped_deck = earthman
|
instance.equipped_deck = earthman
|
||||||
instance.save(update_fields=['equipped_trinket', 'equipped_deck'])
|
instance.save(update_fields=['equipped_trinket', 'equipped_deck'])
|
||||||
|
if earthman:
|
||||||
|
instance.unlocked_decks.add(earthman)
|
||||||
|
|||||||
@@ -68,8 +68,10 @@ body.page-gameboard {
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: center;
|
||||||
|
gap: 0.75rem;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
&::-webkit-scrollbar { display: none; }
|
&::-webkit-scrollbar { display: none; }
|
||||||
|
|||||||
Reference in New Issue
Block a user