diff --git a/src/functional_tests/test_my_notes.py b/src/functional_tests/test_applet_my_notes.py
similarity index 100%
rename from src/functional_tests/test_my_notes.py
rename to src/functional_tests/test_applet_my_notes.py
diff --git a/src/functional_tests/test_my_sky.py b/src/functional_tests/test_applet_my_sky.py
similarity index 67%
rename from src/functional_tests/test_my_sky.py
rename to src/functional_tests/test_applet_my_sky.py
index 9c246b2..230a366 100644
--- a/src/functional_tests/test_my_sky.py
+++ b/src/functional_tests/test_applet_my_sky.py
@@ -5,6 +5,8 @@ natus (natal chart) interface where the user can save their personal sky
to their account (stored on the User model, independent of any game room).
"""
+import json as _json
+
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
@@ -198,3 +200,102 @@ class MySkyAppletWheelTest(FunctionalTest):
.value_of_css_property("display"),
"block",
))
+
+
+class MySkyAppletFormTest(FunctionalTest):
+ """My Sky applet shows natus entry form when no sky data is saved."""
+
+ def setUp(self):
+ super().setUp()
+ Applet.objects.get_or_create(
+ slug="my-sky",
+ defaults={"name": "My Sky", "grid_cols": 6, "grid_rows": 6, "context": "dashboard"},
+ )
+ self.gamer = User.objects.create(email="stargazer@test.io")
+
+ # ── T4 ───────────────────────────────────────────────────────────────────
+
+ def test_applet_shows_entry_form_when_no_sky_saved(self):
+ """When no sky data is saved the My Sky applet shows all natus form
+ fields and a disabled SAVE SKY button; no wheel is drawn yet."""
+ self.create_pre_authenticated_session("stargazer@test.io")
+ self.browser.get(self.live_server_url)
+
+ applet = self.wait_for(
+ lambda: self.browser.find_element(By.ID, "id_applet_my_sky")
+ )
+
+ applet.find_element(By.ID, "id_nf_date")
+ applet.find_element(By.ID, "id_nf_time")
+ applet.find_element(By.ID, "id_nf_place")
+ applet.find_element(By.ID, "id_nf_lat")
+ applet.find_element(By.ID, "id_nf_lon")
+ applet.find_element(By.ID, "id_nf_tz")
+ applet.find_element(By.ID, "id_natus_confirm")
+
+ self.assertFalse(applet.find_elements(By.CSS_SELECTOR, ".nw-root"))
+
+ # ── T5 ───────────────────────────────────────────────────────────────────
+
+ def test_applet_form_disappears_and_wheel_draws_after_save(self):
+ """Filling the applet form and clicking SAVE SKY hides the form wrap
+ and draws the natal wheel in its place."""
+ self.create_pre_authenticated_session("stargazer@test.io")
+ self.browser.get(self.live_server_url)
+
+ self.wait_for(
+ lambda: self.browser.find_element(By.ID, "id_applet_sky_form_wrap")
+ )
+
+ # Mock fetch: preview → chart fixture; save → {saved: true}
+ self.browser.execute_script("""
+ const FIXTURE = """ + _json.dumps(_CHART_FIXTURE) + """;
+ window._origFetch = window.fetch;
+ window.fetch = function(url, opts) {
+ if (url.includes('/sky/preview/')) {
+ return Promise.resolve({
+ ok: true,
+ json: () => Promise.resolve(FIXTURE),
+ });
+ }
+ if (url.includes('/sky/save/')) {
+ return Promise.resolve({
+ ok: true,
+ json: () => Promise.resolve({saved: true}),
+ });
+ }
+ return window._origFetch(url, opts);
+ };
+ """)
+
+ # Fill required fields and fire input to trigger schedulePreview
+ self.browser.execute_script("""
+ document.getElementById('id_nf_date').value = '1990-06-15';
+ document.getElementById('id_nf_lat').value = '51.5074';
+ document.getElementById('id_nf_lon').value = '-0.1278';
+ document.getElementById('id_nf_tz').value = 'Europe/London';
+ document.getElementById('id_nf_date').dispatchEvent(
+ new Event('input', {bubbles: true})
+ );
+ """)
+
+ # Wait for confirm button to be enabled (preview resolved)
+ confirm_btn = self.browser.find_element(By.ID, "id_natus_confirm")
+ self.wait_for(lambda: self.assertIsNone(
+ confirm_btn.get_attribute("disabled")
+ ))
+
+ confirm_btn.click()
+
+ # Form wrap should become hidden
+ form_wrap = self.browser.find_element(By.ID, "id_applet_sky_form_wrap")
+ self.wait_for(lambda: self.assertEqual(
+ form_wrap.value_of_css_property("display"), "none"
+ ))
+
+ # Natal wheel should be drawn inside the applet
+ self.wait_for(lambda: self.assertTrue(
+ self.browser.find_element(
+ By.CSS_SELECTOR, "#id_applet_my_sky .nw-root"
+ )
+ ))
diff --git a/src/functional_tests/test_simple_note_creation.py b/src/functional_tests/test_applet_new_note.py
similarity index 100%
rename from src/functional_tests/test_simple_note_creation.py
rename to src/functional_tests/test_applet_new_note.py
diff --git a/src/functional_tests/test_note_item_validation.py b/src/functional_tests/test_applet_new_note_item_validation.py
similarity index 100%
rename from src/functional_tests/test_note_item_validation.py
rename to src/functional_tests/test_applet_new_note_item_validation.py
diff --git a/src/functional_tests/test_gatekeeper.py b/src/functional_tests/test_room_gatekeeper.py
similarity index 100%
rename from src/functional_tests/test_gatekeeper.py
rename to src/functional_tests/test_room_gatekeeper.py
diff --git a/src/functional_tests/test_pick_sky.py b/src/functional_tests/test_room_sky_select.py
similarity index 100%
rename from src/functional_tests/test_pick_sky.py
rename to src/functional_tests/test_room_sky_select.py
diff --git a/src/static_src/scss/_natus.scss b/src/static_src/scss/_natus.scss
index 9d72709..4d6c1eb 100644
--- a/src/static_src/scss/_natus.scss
+++ b/src/static_src/scss/_natus.scss
@@ -522,6 +522,21 @@ body[class*="-light"] #id_natus_tooltip {
max-height: none;
align-self: center;
}
+
+ #id_applet_sky_form_wrap {
+ flex: 1;
+ min-height: 0;
+ overflow-y: auto;
+ padding: 0.5rem 0.25rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ #id_natus_confirm {
+ flex-shrink: 0;
+ width: 100%;
+ }
+ }
}
// ── Sky full page (aperture + column layout) ──────────────────────────────────
diff --git a/src/templates/apps/dashboard/_partials/_applet-my-sky.html b/src/templates/apps/dashboard/_partials/_applet-my-sky.html
index bba6da5..7110e26 100644
--- a/src/templates/apps/dashboard/_partials/_applet-my-sky.html
+++ b/src/templates/apps/dashboard/_partials/_applet-my-sky.html
@@ -1,25 +1,345 @@
{% load static %}
My Sky
+
+ {% if not request.user.sky_chart_data %}
+