diff --git a/src/apps/dashboard/static/apps/dashboard/dashboard.js b/src/apps/dashboard/static/apps/dashboard/dashboard.js index f15e13f..42ede9d 100644 --- a/src/apps/dashboard/static/apps/dashboard/dashboard.js +++ b/src/apps/dashboard/static/apps/dashboard/dashboard.js @@ -33,17 +33,26 @@ const bindPaletteSwatches = () => { function showTooltip(swatch) { if (!portal) return; - const label = swatch.dataset.label || ''; - const locked = swatch.dataset.locked === 'true'; - const date = swatch.dataset.unlockedDate || ''; - const shoptalk = swatch.dataset.shoptalk || ''; - const lockIcon = locked ? 'fa-lock' : 'fa-lock-open'; - const lockText = locked ? 'Locked' : `Unlocked — ${date}`.trim(); + const label = swatch.dataset.label || ''; + const locked = swatch.dataset.locked === 'true'; + const description = swatch.dataset.description || ''; + const unlockedDate = swatch.dataset.unlockedDate || ''; + const lockIcon = locked ? 'fa-lock' : 'fa-lock-open'; + const lockText = locked ? 'Locked' : 'Unlocked'; + + let dateLine = ''; + if (unlockedDate) { + const dt = new Date(unlockedDate); + const dateStr = dt.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + const timeStr = dt.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); + dateLine = `

${dateStr} · ${timeStr}

`; + } portal.innerHTML = `

${label}

- ${shoptalk ? `

${shoptalk}

` : ''} -

${lockText}

`; + ${description ? `

${description}

` : ''} +

${lockText}

+ ${dateLine}`; const rect = swatch.getBoundingClientRect(); portal.style.display = 'block'; diff --git a/src/apps/dashboard/tests/integrated/test_views.py b/src/apps/dashboard/tests/integrated/test_views.py index 22a0ac1..a7c4ab1 100644 --- a/src/apps/dashboard/tests/integrated/test_views.py +++ b/src/apps/dashboard/tests/integrated/test_views.py @@ -358,7 +358,7 @@ class NotePaletteContextTest(TestCase): bardo = next(p for p in palettes if p["name"] == "palette-bardo") self.assertFalse(bardo["locked"]) - def test_note_palette_shoptalk_contains_note_title(self): + def test_note_palette_description_contains_note_title(self): Note.objects.create( user=self.user, slug="stargazer", earned_at=timezone.now(), palette="palette-bardo", @@ -366,7 +366,41 @@ class NotePaletteContextTest(TestCase): response = self.client.get("/") palettes = response.context["palettes"] bardo = next(p for p in palettes if p["name"] == "palette-bardo") - self.assertIn("Stargazer", bardo["shoptalk"]) + self.assertIn("Stargazer", bardo["description"]) + + def test_default_palette_description_is_available_by_default(self): + response = self.client.get("/") + palettes = response.context["palettes"] + default = next(p for p in palettes if p["name"] == "palette-default") + self.assertEqual(default["description"], "available by default") + + def test_locked_palette_description_is_explore_to_unlock(self): + response = self.client.get("/") + palettes = response.context["palettes"] + bardo = next(p for p in palettes if p["name"] == "palette-bardo") + self.assertEqual(bardo["description"], "explore to unlock") + + def test_note_palette_description_is_recognized_via(self): + Note.objects.create( + user=self.user, slug="stargazer", earned_at=timezone.now(), + palette="palette-bardo", + ) + response = self.client.get("/") + palettes = response.context["palettes"] + bardo = next(p for p in palettes if p["name"] == "palette-bardo") + self.assertEqual(bardo["description"], "recognized via Stargazer") + + def test_note_palette_entry_includes_unlocked_date_iso(self): + earned = timezone.now() + Note.objects.create( + user=self.user, slug="stargazer", earned_at=earned, + palette="palette-bardo", + ) + response = self.client.get("/") + palettes = response.context["palettes"] + bardo = next(p for p in palettes if p["name"] == "palette-bardo") + self.assertIn("unlocked_date", bardo) + self.assertEqual(bardo["unlocked_date"], earned.isoformat()) def test_note_without_palette_field_keeps_swatch_locked(self): Note.objects.create( diff --git a/src/apps/dashboard/views.py b/src/apps/dashboard/views.py index 8caded3..bb3c1f5 100644 --- a/src/apps/dashboard/views.py +++ b/src/apps/dashboard/views.py @@ -51,7 +51,10 @@ PALETTES = _PALETTE_DEFS def _palettes_for_user(user): if not (user and user.is_authenticated): - return [dict(p, shoptalk="Placeholder") for p in _PALETTE_DEFS] + return [ + dict(p, description="available by default" if not p["locked"] else "explore to unlock") + for p in _PALETTE_DEFS + ] granted = { r.palette: r for r in Note.objects.filter(user=user, palette__isnull=False).exclude(palette="") @@ -63,9 +66,12 @@ def _palettes_for_user(user): if r and p["locked"]: entry["locked"] = False title = _NOTE_TITLES.get(r.slug, r.slug.capitalize()) - entry["shoptalk"] = f"{title} · {r.earned_at.strftime('%b %d, %Y').replace(' 0', ' ')}" + entry["description"] = f"recognized via {title}" + entry["unlocked_date"] = r.earned_at.isoformat() + elif not p["locked"]: + entry["description"] = "available by default" else: - entry["shoptalk"] = "Placeholder" + entry["description"] = "explore to unlock" result.append(entry) return result diff --git a/src/static_src/scss/_palette-picker.scss b/src/static_src/scss/_palette-picker.scss index abcc532..948777b 100644 --- a/src/static_src/scss/_palette-picker.scss +++ b/src/static_src/scss/_palette-picker.scss @@ -80,7 +80,8 @@ #id_tooltip_portal { // Override .tt { display: none } — portal content is shown/hidden by JS .tt-title, - .tt-shoptalk, + .tt-description, + .tt-date, .tt-lock { display: block; } diff --git a/src/static_src/scss/_tooltips.scss b/src/static_src/scss/_tooltips.scss index ab9e4c3..bf2bb94 100644 --- a/src/static_src/scss/_tooltips.scss +++ b/src/static_src/scss/_tooltips.scss @@ -10,6 +10,7 @@ .tt-description { padding: 0.125rem; font-size: 0.75rem; } .tt-shoptalk { font-size: 0.75rem; opacity: 0.75; } .tt-expiry { font-size: 1rem; color: rgba(var(--priRd), 1); } + .tt-date { font-size: 1rem; color: rgba(var(--priGn), 1); } } .token-tooltip, diff --git a/src/templates/apps/dashboard/_partials/_applet-palette.html b/src/templates/apps/dashboard/_partials/_applet-palette.html index 76a1b5d..e961fa1 100644 --- a/src/templates/apps/dashboard/_partials/_applet-palette.html +++ b/src/templates/apps/dashboard/_partials/_applet-palette.html @@ -12,8 +12,8 @@ data-palette="{{ palette.name }}" data-label="{{ palette.label }}" data-locked="{{ palette.locked|yesno:'true,false' }}" - data-unlocked-date="{% if not palette.locked %}Default{% endif %}" - data-shoptalk="{{ palette.shoptalk }}" + data-unlocked-date="{{ palette.unlocked_date|default:'' }}" + data-description="{{ palette.description }}" > {% if not palette.locked %}