diff --git a/assets/management.css b/assets/management.css index 3f85f40..e91b074 100644 --- a/assets/management.css +++ b/assets/management.css @@ -1737,23 +1737,73 @@ } } -/* --- Club identity live preview (management/templates/management/club_settings.html) --- - A mini mock of this app's own sidebar + a sample button -- genuinely what - primary_color/secondary_color affect (base.html's --tenant-* custom - properties), not a fabricated App/Website/Email mockup. Colours are set - inline by that template's own script as the form fields change; only - layout/typography lives here. */ +/* --- Club identity page (management/templates/management/club_settings.html) --- + Colours: swatch + mono hex merged into one bordered row (D9's own look), + not a labelled field with a decorative swatch floating below it. Live + preview: a mini mock of this app's own sidebar/button (the real, live + surface primary_color/secondary_color affect today, via base.html's + --tenant-* custom properties) plus a mobile app mock (the same colours + applied to the surface that's built next) -- not D9's fabricated App/ + Website/Email tabs. Colours are set inline by that template's own script + as the form fields change; only layout/typography lives here. */ @layer components { + .club-color-row { + display: flex; + align-items: center; + gap: 0.625rem; + border: 1px solid var(--color-edge); + border-radius: 0.5rem; + padding: 0.5rem 0.625rem; + } + .club-color-swatch { - width: 2rem; - height: 1.5rem; + width: 1.625rem; + height: 1.625rem; + flex-shrink: 0; padding: 0; - border: 1px solid var(--color-line); + border: none; border-radius: 0.375rem; background: none; cursor: pointer; } + .club-color-hex-input { + flex: 1; + min-width: 0; + border: none; + background: none; + outline: none; + padding: 0; + font-family: var(--font-mono); + font-size: 0.875rem; + color: var(--color-ink); + } + + .club-contrast-box { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + border-radius: 0.5rem; + padding: 0.625rem 0.75rem; + font-size: 0.8125rem; + transition: background-color 0.15s ease, color 0.15s ease; + } + + .club-logo-dropzone { + display: flex; + width: 4.75rem; + height: 4.75rem; + flex-shrink: 0; + align-items: center; + justify-content: center; + overflow: hidden; + border: 1.5px dashed var(--color-stroke); + border-radius: 0.5rem; + background: var(--color-paper); + } + .preview-frame { display: flex; height: 220px; @@ -1856,10 +1906,119 @@ width: 60%; } - .contrast-icon { - display: inline-flex; - width: 1rem; + /* The mobile app mock -- not built yet, but its header/role-switcher take + the same primary/secondary colours the sidebar mock above does, so this + preview stays accurate once it exists rather than needing a rework. */ + .preview-phone { + width: 11rem; + flex-shrink: 0; + overflow: hidden; + border: 5px solid var(--color-ink); + border-radius: 1.375rem; + background: var(--color-paper); + } + + .preview-phone-header { + padding: 0.75rem 0.625rem 0.625rem; + background: var(--color-ink); + color: #fff; + transition: background-color 0.15s ease, color 0.15s ease; + } + + .preview-phone-header-row { + display: flex; + align-items: center; + gap: 0.375rem; + } + + .preview-crest-sm { + width: 1.125rem; + height: 1.125rem; + font-size: 0.5rem; + border-radius: 0.25rem; + } + + .preview-phone-name { + font-family: var(--font-display); + font-size: 0.6875rem; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; + } + + .preview-phone-roles { + display: flex; + gap: 2px; + margin-top: 0.5rem; + padding: 2px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.12); + } + + .preview-phone-role { + flex: 1; + display: flex; + align-items: center; justify-content: center; - font-weight: 700; + height: 1.25rem; + border-radius: 999px; + font-family: var(--font-display); + font-size: 0.5625rem; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--color-on-dark); + } + + .preview-phone-role-active { + background: #fff; + color: var(--color-ink); + } + + .preview-phone-body { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.625rem; + } + + .preview-phone-card { + border-radius: 0.5rem; + padding: 0.5rem; + background: var(--color-club); + color: #fff; + transition: background-color 0.15s ease, color 0.15s ease; + } + + .preview-phone-eyebrow { + font-family: var(--font-display); + font-size: 0.5625rem; + font-weight: 800; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--color-info); + } + + .preview-phone-title { + margin-top: 0.125rem; + font-family: var(--font-display); + font-size: 0.75rem; + line-height: 1.05; + font-weight: 800; + text-transform: uppercase; + } + + .preview-phone-bar { + height: 1rem; + margin-top: 0.5rem; + border-radius: 0.25rem; + background: var(--color-ok); + } + + .preview-phone-skeleton-card { + border: 1px solid var(--color-line); + border-radius: 0.5rem; + padding: 0.5rem; + background: #fff; } } diff --git a/club/migrations/0027_club_website.py b/club/migrations/0027_club_website.py new file mode 100644 index 0000000..7ca83c0 --- /dev/null +++ b/club/migrations/0027_club_website.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-08-20 07:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0026_memberrequirementstatus_is_bypassed_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='club', + name='website', + field=models.URLField(blank=True, help_text="The club's own site, if it has one -- shown alongside its RosterChief pages, not used for anything else yet.", verbose_name='website'), + ), + ] diff --git a/club/models.py b/club/models.py index 2e37b54..3ed2e0c 100644 --- a/club/models.py +++ b/club/models.py @@ -48,6 +48,7 @@ class Club(UUIDModel): blank=True, help_text=_("The club's public address, shown to people the club writes to or asks to get in touch -- e.g. a parent claiming a child. Falls back to nothing being shown at all, so it's worth setting."), ) + website = models.URLField(_("website"), blank=True, help_text=_("The club's own site, if it has one -- shown alongside its RosterChief pages, not used for anything else yet.")) logo = models.FileField( _("logo"), diff --git a/controlpanel/forms.py b/controlpanel/forms.py index 188e134..d48faad 100644 --- a/controlpanel/forms.py +++ b/controlpanel/forms.py @@ -14,7 +14,7 @@ from .services.admins import find_member_by_email class ClubForm(forms.ModelForm): class Meta: model = Club - fields = ["name", "legal_name", "contact_email", "slug", "sport_type", "logo", "primary_color", "secondary_color", "season_start", "season_duration_months"] + fields = ["name", "legal_name", "contact_email", "website", "slug", "sport_type", "logo", "primary_color", "secondary_color", "season_start", "season_duration_months"] help_texts = {"slug": _("Drives the club's subdomain. Left blank, it is derived from the name.")} # Deliberately a text input, not : a colour picker cannot # express "no colour" -- it would submit #000000 for every club that never diff --git a/controlpanel/templates/controlpanel/club_form.html b/controlpanel/templates/controlpanel/club_form.html index 8486fb5..050c56a 100644 --- a/controlpanel/templates/controlpanel/club_form.html +++ b/controlpanel/templates/controlpanel/club_form.html @@ -27,6 +27,7 @@ {% form_field form.name %} {% form_field form.legal_name %} {% form_field form.contact_email %} + {% form_field form.website %} {% form_field form.slug %} {% form_field form.sport_type %} diff --git a/management/forms.py b/management/forms.py index f60647d..5f81d8f 100644 --- a/management/forms.py +++ b/management/forms.py @@ -881,7 +881,7 @@ class ClubSettingsForm(forms.ModelForm): class Meta: model = Club - fields = ["name", "legal_name", "contact_email", "logo", "primary_color", "secondary_color"] + fields = ["name", "legal_name", "contact_email", "website", "logo", "primary_color", "secondary_color"] widgets = { "primary_color": forms.TextInput(attrs={"placeholder": "#1e40af"}), "secondary_color": forms.TextInput(attrs={"placeholder": "#be185d"}), diff --git a/management/templates/management/club_settings.html b/management/templates/management/club_settings.html index 685f5dc..704b96d 100644 --- a/management/templates/management/club_settings.html +++ b/management/templates/management/club_settings.html @@ -5,16 +5,18 @@ A club's own self-service identity/branding editor -- see management/forms.py's ClubSettingsForm for exactly which fields are editable here versus platform-staff-only (slug, seasons). D9 "Club - identity & branding" in the design handoff is the reference: two columns, - form fields on the left and a live preview on the right. The preview here - is scoped to what actually exists in this app rather than D9's aspirational - App/Website/Email tabs -- primary_color drives the sidebar - (--tenant-sidebar-bg/fg, base.html), secondary_color drives the club accent - (--tenant-club/-dark/-content, buttons/active nav/crest) -- so the mini - sidebar + button mock below is genuinely what changing these fields does, - not a fabricated mockup. The colour math (content colour, contrast ratio) - mirrors Club._content_color_for exactly, so the "computed ratio" shown is - the real number the server will use, not an approximation. + identity & branding" in the design handoff is the reference, followed + closely for the Colours card (merged swatch+hex row, side-by-side + contrast-check boxes) and the Logo card (dashed drop zone) -- markup + pulled straight from the design canvas (RosterChief Platform.dc.html), + not the prose summary. The live preview is scoped to surfaces that are + (or will genuinely be) this app rather than D9's aspirational App/ + Website/Email tabs: the sidebar mock is what primary_color/secondary_color + affect today (base.html's --tenant-* custom properties); the phone mock + is the same colours applied ahead of the mobile app being built. The + colour math (content colour, contrast ratio) mirrors + Club._content_color_for exactly, so what's shown is the real number the + server computes on save, not an approximation. {% endcomment %} {% block panel_title %}{% trans "Club identity" %}{% endblock panel_title %} @@ -39,10 +41,11 @@
{{ form.primary_color.errors|join:" " }}
{% endif %}{{ form.secondary_color.errors|join:" " }}
{% endif %}{% trans "Where these colours actually show up in the management app -- updates as you edit, before you save anything." %}
+{% trans "Where these colours actually show up -- updates as you edit, before you save anything." %}
-