Skip to content

Commit 04e7f6e

Browse files
committed
planner note, tabs for event and shift form
1 parent d081d59 commit 04e7f6e

7 files changed

Lines changed: 489 additions & 319 deletions

File tree

src/ephios/core/forms/events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class EventForm(forms.ModelForm):
5959

6060
class Meta:
6161
model = Event
62-
fields = ["title", "type", "description", "location"]
62+
fields = ["title", "type", "description", "location", "planner_note"]
6363
widgets = {"description": MarkdownTextarea}
6464

6565
def __init__(self, **kwargs):
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 5.2.12 on 2026-04-24 14:00
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("core", "0039_alter_userprofile_disabled_notifications"),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name="event",
14+
name="planner_note",
15+
field=models.TextField(blank=True, null=True, verbose_name="planner note"),
16+
),
17+
]

src/ephios/core/models/events.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ class Event(Model):
9191
description = TextField(_("description"), blank=True, null=True)
9292
location = CharField(_("location"), max_length=254)
9393
type = ForeignKey(EventType, on_delete=models.CASCADE, verbose_name=_("event type"))
94+
planner_note = models.TextField(
95+
_("planner note"),
96+
blank=True,
97+
null=True,
98+
help_text=_("Note only visible to responsibles of this event."),
99+
)
94100
active = BooleanField(default=False, verbose_name=_("active"))
95101
group_object_permission_set = GenericRelation(
96102
GroupObjectPermission, object_id_field="object_pk"

src/ephios/core/templates/core/event_form.html

Lines changed: 96 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,53 +21,104 @@
2121
{% render_alert unsaved_message|add:" ("|add:event.title|add:"). <a class='btn btn-light' href='"|add:event.get_absolute_url|add:"'>"|add:view_message|add:"</a>"|safe %}
2222
{% endfor %}
2323
{% endif %}
24-
<div class="page-header">
25-
{% if event.id %}
26-
<h1>{% translate "Edit event" %}</h1>
27-
{% else %}
28-
<h1>
29-
{% blocktranslate trimmed with title=eventtype.title %}
30-
Create new {{ title }}
31-
{% endblocktranslate %}
32-
</h1>
33-
{% endif %}
34-
</div>
35-
<form method="post" class="form">
36-
{% csrf_token %}
37-
{{ form.title|as_crispy_field }}
38-
{% if form.type %}
39-
{{ form.type|as_crispy_field }}
40-
{% endif %}
41-
{{ form.description|as_crispy_field }}
42-
{{ form.location|as_crispy_field }}
43-
44-
<div class="card mb-3">
45-
<div class="card-header collapsed" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
46-
<a href="#">{% translate "Permissions" %}</a>
24+
<div class="row">
25+
<div class="col">
26+
<div class="page-header">
27+
{% if event.id %}
28+
<h1>{% translate "Edit event" %}</h1>
29+
{% else %}
30+
<h1>
31+
{% blocktranslate trimmed with title=eventtype.title %}
32+
Create new {{ title }}
33+
{% endblocktranslate %}
34+
</h1>
35+
{% endif %}
4736
</div>
48-
<div id="advancedSettings" class="collapse">
49-
<div class="card-body">
50-
{{ form.visible_for|as_crispy_field }}
51-
{{ form.responsible_groups|as_crispy_field }}
52-
{{ form.responsible_users|as_crispy_field }}
53-
</div>
54-
</div>
55-
</div>
37+
<form method="post" class="form">
38+
{% csrf_token %}
39+
<nav>
40+
<ul class="nav nav-tabs" id="event-form-tabs" role="tablist">
41+
<li class="nav-item" role="presentation">
42+
<button class="nav-link active" id="general-tab" data-bs-toggle="tab"
43+
data-bs-target="#general-tab-pane"
44+
type="button" role="tab" aria-controls="general-tab-pane" aria-selected="true">
45+
{% translate "General" %}
46+
</button>
47+
</li>
48+
<li class="nav-item" role="presentation">
49+
<button class="nav-link" id="permissions-tab" data-bs-toggle="tab"
50+
data-bs-target="#permissions-tab-pane"
51+
type="button" role="tab" aria-controls="permissions-tab-pane" aria-selected="false">
52+
{% translate "Permissions" %}
53+
</button>
54+
</li>
55+
<li class="nav-item" role="presentation">
56+
<button class="nav-link" id="plugins-tab" data-bs-toggle="tab"
57+
data-bs-target="#plugins-tab-pane"
58+
type="button" role="tab" aria-controls="plugins-tab-pane" aria-selected="false">
59+
{% translate "Functions" %}
60+
</button>
61+
</li>
62+
</ul>
63+
</nav>
5664

57-
{% for plugin_form in plugin_forms %}
58-
{{ plugin_form }}
59-
{% endfor %}
65+
<div class="tab-content" id="event-form-panes">
66+
<div class="tab-pane active" id="general-tab-pane" role="tabpanel" aria-labelledby="general-tab"
67+
tabindex="0">
68+
{{ form.title|as_crispy_field }}
69+
{% if form.type %}
70+
{{ form.type|as_crispy_field }}
71+
{% endif %}
72+
{{ form.description|as_crispy_field }}
73+
{{ form.location|as_crispy_field }}
74+
</div>
6075

61-
<div class="form-group">
62-
{% if event.id %}
63-
<a role="button" class="btn btn-secondary"
64-
href="{{ event.get_absolute_url }}">{% translate "Back" %}</a>
65-
<button type="submit" class="btn btn-primary float-end">{% translate "Save" %}</button>
66-
{% else %}
67-
<a role="button" class="btn btn-secondary"
68-
href="{% url "core:event_list" %}">{% translate "Cancel" %}</a>
69-
<button type="submit" class="btn btn-primary float-end">{% translate "Next" %}</button>
70-
{% endif %}
76+
<div class="tab-pane" id="permissions-tab-pane" role="tabpanel"
77+
aria-labelledby="permissions-tab"
78+
tabindex="0">
79+
{{ form.visible_for|as_crispy_field }}
80+
{{ form.responsible_groups|as_crispy_field }}
81+
{{ form.responsible_users|as_crispy_field }}
82+
{{ form.planner_note|as_crispy_field }}
83+
</div>
84+
<div class="tab-pane" id="plugins-tab-pane" role="tabpanel" aria-labelledby="plugins-tab"
85+
tabindex="0">
86+
{% for plugin_form in plugin_forms %}
87+
{{ plugin_form }}
88+
{% endfor %}
89+
</div>
90+
</div>
91+
92+
<div class="form-group mt-3">
93+
{% if event.id %}
94+
<a role="button" class="btn btn-secondary"
95+
href="{{ event.get_absolute_url }}">{% translate "Back" %}</a>
96+
<button type="submit" class="btn btn-primary float-end">{% translate "Save" %}</button>
97+
{% else %}
98+
<a role="button" class="btn btn-secondary"
99+
href="{% url "core:event_list" %}">{% translate "Cancel" %}</a>
100+
<button type="submit" class="btn btn-primary float-end">{% translate "Next" %}</button>
101+
{% endif %}
102+
</div>
103+
</form>
71104
</div>
72-
</form>
105+
{% if event.id %}
106+
<div class="d-none d-lg-block col-3">
107+
<div class="card mb-2 border-info">
108+
<div class="card-body">
109+
<h5 class="card-title">
110+
{{ event.title }}
111+
</h5>
112+
<p class="card-text">
113+
{% translate "Event type" %}: {{ event.type }}<br>
114+
{% translate "Location" %}: {{ event.location }}
115+
</p>
116+
</div>
117+
</div>
118+
{% for shift in event.shifts.all %}
119+
{% include "core/fragments/shift_box_small.html" with shift=shift editing_shift=None can_change=True %}
120+
{% endfor %}
121+
</div>
122+
{% endif %}
123+
</div>
73124
{% endblock %}

src/ephios/core/templates/core/shift_form.html

Lines changed: 111 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,81 +13,127 @@
1313
{% endblock %}
1414

1515
{% block content %}
16-
<div class="page-header">
17-
{% if object %}
18-
<h1>{% translate "Edit shift" %}</h1>
19-
{% else %}
20-
<h1>{% translate "Create new shift for event" %}</h1>
21-
{% endif %}
22-
</div>
2316
<div class="row">
24-
<div class="col-md-9">
17+
<div class="col">
18+
<div class="page-header">
19+
{% if object %}
20+
<h1>{% translate "Edit shift" %}</h1>
21+
{% else %}
22+
<h1>{% translate "Create new shift for event" %}</h1>
23+
{% endif %}
24+
</div>
2525
<form method="post" class="form">
2626
{% csrf_token %}
27-
<div class="row">
28-
{% crispy_field form.date wrapper_class="col-12 col-lg-6" %}
29-
{% crispy_field form.meeting_time wrapper_class="col-12 col-lg-6" %}
30-
{% crispy_field form.start_time wrapper_class="col-12 col-lg-6" %}
31-
{% crispy_field form.end_time wrapper_class="col-12 col-lg-6" %}
32-
</div>
33-
{% crispy_field form.label %}
34-
<hr class="border border-secondary border-dark opacity-75">
35-
<h2>
36-
{% translate "Signup mode" %}
37-
</h2>
38-
{% crispy_field form.signup_flow_slug show_labels=False %}
39-
<div data-ajax-replace-select="id_signup_flow_slug"
40-
{% url "core:signup_flow_configuration_form" event.id "SELECT_VALUE" as flow_url %}
41-
data-ajax-replace-url="{{ flow_url }}{% if object %}?shift_id={{ object.id }}{% endif %}">
42-
{% include 'core/fragments/shift_signup_config_form.html' with form=flow_configuration_form %}
43-
</div>
44-
<hr class="border border-secondary border-dark opacity-75">
45-
<h2>
46-
{% translate "Structure" %}
47-
</h2>
48-
{% crispy_field form.structure_slug show_labels=False %}
49-
<div data-ajax-replace-select="id_structure_slug"
50-
{% url "core:shift_structure_configuration_form" event.id "SELECT_VALUE" as structure_url %}
51-
data-ajax-replace-url="{{ structure_url }}{% if object %}?shift_id={{ object.id }}{% endif %}">
52-
{% include 'core/fragments/shift_signup_config_form.html' with form=structure_configuration_form %}
53-
</div>
54-
<hr class="border border-secondary border-dark opacity-75">
55-
<div class="plugin-forms">
56-
{% for plugin_form in plugin_forms %}
57-
{{ plugin_form }}
58-
{% endfor %}
27+
28+
<nav>
29+
<ul class="nav nav-tabs" id="event-form-tabs" role="tablist">
30+
<li class="nav-item" role="presentation">
31+
<button class="nav-link active" id="general-tab" data-bs-toggle="tab"
32+
data-bs-target="#general-tab-pane"
33+
type="button" role="tab" aria-controls="general-tab-pane" aria-selected="true">
34+
{% translate "General" %}
35+
</button>
36+
</li>
37+
<li class="nav-item" role="presentation">
38+
<button class="nav-link" id="signup-tab" data-bs-toggle="tab"
39+
data-bs-target="#signup-tab-pane"
40+
type="button" role="tab" aria-controls="signup-tab-pane" aria-selected="false">
41+
{% translate "Signup" %}
42+
</button>
43+
</li>
44+
<li class="nav-item" role="presentation">
45+
<button class="nav-link" id="structure-tab" data-bs-toggle="tab"
46+
data-bs-target="#structure-tab-pane"
47+
type="button" role="tab" aria-controls="structure-tab-pane" aria-selected="false">
48+
{% translate "Personnel" %}
49+
</button>
50+
</li>
51+
</ul>
52+
</nav>
53+
<div class="tab-content" id="event-form-panes">
54+
<div class="tab-pane active" id="general-tab-pane" role="tabpanel" aria-labelledby="general-tab"
55+
tabindex="0">
56+
<div class="row">
57+
{% crispy_field form.date wrapper_class="col-12 col-lg-6" %}
58+
{% crispy_field form.meeting_time wrapper_class="col-12 col-lg-6" %}
59+
{% crispy_field form.start_time wrapper_class="col-12 col-lg-6" %}
60+
{% crispy_field form.end_time wrapper_class="col-12 col-lg-6" %}
61+
</div>
62+
{% crispy_field form.label %}
63+
<div class="plugin-forms">
64+
{% for plugin_form in plugin_forms %}
65+
{{ plugin_form }}
66+
{% endfor %}
67+
</div>
68+
</div>
69+
<div class="tab-pane" id="signup-tab-pane" role="tabpanel"
70+
aria-labelledby="signup-tab"
71+
tabindex="0">
72+
<label for="{{ form.signup_flow_slug.id_for_label }}" class="form-label">
73+
{% translate "Choose a mode of signup" %}
74+
</label>
75+
{% crispy_field form.signup_flow_slug show_labels=False %}
76+
<div data-ajax-replace-select="id_signup_flow_slug"
77+
{% url "core:signup_flow_configuration_form" event.id "SELECT_VALUE" as flow_url %}
78+
data-ajax-replace-url="{{ flow_url }}{% if object %}?shift_id={{ object.id }}{% endif %}">
79+
{% include 'core/fragments/shift_signup_config_form.html' with form=flow_configuration_form %}
80+
</div>
81+
</div>
82+
<div class="tab-pane" id="structure-tab-pane" role="tabpanel"
83+
aria-labelledby="structure-tab"
84+
tabindex="0">
85+
<label for="{{ form.structure_slug.id_for_label }}" class="form-label">
86+
{% translate "Choose a model for specifying personnel requirements" %}
87+
</label>
88+
89+
{% crispy_field form.structure_slug show_labels=False %}
90+
<div data-ajax-replace-select="id_structure_slug"
91+
{% url "core:shift_structure_configuration_form" event.id "SELECT_VALUE" as structure_url %}
92+
data-ajax-replace-url="
93+
{{ structure_url }}{% if object %}?shift_id={{ object.id }}{% endif %}">
94+
{% include 'core/fragments/shift_signup_config_form.html' with form=structure_configuration_form %}
95+
</div>
96+
</div>
5997
</div>
60-
<div class="form-group">
61-
{% if object or event.active %}
62-
<a role="button" class="btn btn-secondary"
63-
href="{{ event.get_absolute_url }}">
64-
<span class="fas fa-arrow-left"></span> {% translate "Back to event" %}
65-
</a>
66-
{% if object and object.event.shifts.count > 1 %}
67-
<a role="button" class="btn btn-danger"
68-
href="{% url "core:shift_delete" object.pk %}"><span
69-
class="fas fa-trash-alt"></span> {% translate "Delete" %}</a>
98+
99+
<div class="row form-group mt-3">
100+
<div class="col-auto">
101+
{% if object or event.active %}
102+
<a role="button" class="btn btn-secondary"
103+
href="{{ event.get_absolute_url }}">
104+
<span class="fas fa-arrow-left"></span> {% translate "Back to event" %}
105+
</a>
106+
107+
{% if object and object.event.shifts.count > 1 %}
108+
<a role="button" class="btn btn-danger"
109+
href="{% url "core:shift_delete" object.pk %}"><span
110+
class="fas fa-trash-alt"></span> {% translate "Delete" %}</a>
111+
{% endif %}
70112
{% endif %}
71-
<button type="submit" class="btn btn-primary float-end ms-1">
72-
<span class="fas fa-save"></span> {% translate "Save" %}
113+
</div>
114+
<div class="col text-end">
115+
<button type="submit" name="copyShift" class="btn btn-secondary mb-1">
116+
<span class="fas fa-copy"></span> {% translate "Copy shift" %}
73117
</button>
74-
{% else %}
75-
<a role="button" class="btn btn-danger"
76-
href="{% url "core:event_delete" event.pk %}">{% translate "Discard event" %}</a>
77-
<button type="submit" class="btn btn-primary float-end ms-1">
78-
<span class="fas fa-save"></span> {% translate "Save event" %}
118+
<button type="submit" name="addAnother" class="btn btn-secondary mb-1">
119+
<span class="fas fa-plus"></span> {% translate "Add another shift" %}
79120
</button>
80-
{% endif %}
81-
<button type="submit" name="addAnother" class="btn btn-secondary float-end">
82-
<span class="fas fa-plus"></span> {% translate "Add another shift" %}
83-
</button>
84-
<button type="submit" name="copyShift" class="btn btn-secondary float-end me-1">
85-
<span class="fas fa-copy"></span> {% translate "Copy shift" %}
86-
</button>
121+
{% if object or event.active %}
122+
<button type="submit" class="btn btn-primary mb-1">
123+
<span class="fas fa-save"></span> {% translate "Save" %}
124+
</button>
125+
{% else %}
126+
<a role="button" class="btn btn-danger"
127+
href="{% url "core:event_delete" event.pk %}">{% translate "Discard event" %}</a>
128+
<button type="submit" class="btn btn-primary mb-1">
129+
<span class="fas fa-save"></span> {% translate "Save event" %}
130+
</button>
131+
{% endif %}
132+
</div>
87133
</div>
88134
</form>
89135
</div>
90-
<div class="col-md-3">
136+
<div class="d-none d-lg-block col-3">
91137
<div class="card mb-2">
92138
<div class="card-body">
93139
<h5 class="card-title">

0 commit comments

Comments
 (0)