Coverage for src / mesh / views / components / stepper.py: 31%
110 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
1from __future__ import annotations
3from dataclasses import dataclass, field
4from typing import TYPE_CHECKING
6from django.urls import reverse
7from django.utils.translation import gettext_lazy as _
9from mesh.views.forms.base_forms import FormAction
11from .button import Button
13if TYPE_CHECKING:
14 from mesh.models.orm.submission_models import Submission
17@dataclass
18class StepConfig:
19 id: str
20 title: str
21 href: str = field(default="")
22 description: str = field(default="")
23 tooltip: str = field(default="")
24 # Whether the step has already been completed
25 completed: bool = field(default=False)
26 # Whether this is the currently displayed/selected step.
27 active: bool = field(default=False)
28 # Whether this is a step below the currently displayed/selected step.
29 below_active: bool = field(default=False)
30 # Whether the step config is navigable.
31 # It can be True for steps after active one if you have used the Previous button or a direct link to a step.
32 can_navigate: bool = field(default=False)
33 # For some steps, the Next button is the form submit button
34 # For others (use_href_for_next=True), the Next button is a simple link
35 use_href_for_next_button: bool = field(default=False)
38@dataclass
39class StepperConfig:
40 steps: list[StepConfig] = field(default_factory=list)
41 title: str = field(default="")
42 # Id of the active step
43 active: str = field(default="")
44 submission: Submission = None
46 def get_step(self, step_id: str) -> StepConfig | None:
47 return next((s for s in self.steps if s.id == step_id), None)
49 def set_active_step(self, step_id: str):
50 below_active_step = True
52 for s in self.steps:
53 s.below_active = below_active_step
55 if s.id == step_id:
56 below_active_step = False
57 s.active = True
58 self.active = step_id
60 def get_next_step(self, step_id):
61 next_step = None
62 i = 0
63 while next_step is None and i < len(self.steps):
64 if self.steps[i].id == step_id:
65 if i + 1 < len(self.steps):
66 next_step = self.steps[i + 1]
67 i += 1
68 return next_step
70 def get_previous_step(self, step_id):
71 previous_step = None
72 i = 0
73 while previous_step is None and i < len(self.steps):
74 if self.steps[i].id == step_id:
75 if i - 1 > -1:
76 previous_step = self.steps[i - 1]
77 i += 1
78 return previous_step
80 def set_completed_steps(self, step_ids: list[str]):
81 for step in self.steps:
82 step.completed = step.id in step_ids
84 def get_next_button(self):
85 step_id = self.active
86 button = None
87 next_step = self.get_next_step(step_id)
88 current_step = self.get_step(step_id)
89 id = "next" # if step_id != "metadata" else "next-from-mesh"
90 data_attrs = {}
91 if step_id == "metadata":
92 data_attrs["data-url"] = [
93 reverse(
94 "mesh:submission_update_vuejs", kwargs={"submission_pk": self.submission.pk}
95 )
96 ]
98 if next_step is not None:
99 current_step = self.get_step(step_id)
100 href = next_step.href if current_step.use_href_for_next_button else None
101 button = add_stepper_button(
102 id=id,
103 title="Next",
104 icon_class="fa-right-long",
105 name=FormAction.NEXT.value,
106 href=href,
107 btn_classes=["button-highlight", "next-button-to-watch"],
108 data_attrs=data_attrs,
109 submit_button=True if step_id != "metadata" else False,
110 )
111 return button
113 def get_previous_button(self):
114 step_id = self.active
115 button = None
116 previous_step = self.get_previous_step(step_id)
117 if previous_step is not None:
118 button = add_stepper_button(
119 id="previous",
120 title="Previous",
121 icon_class="fa-left-long",
122 name=FormAction.PREVIOUS.value,
123 href=previous_step.href,
124 )
125 return button
128def get_submission_stepper(submission: Submission | None) -> StepperConfig:
129 """
130 Returns the submission stepper config adapted with the given submission.
131 The active step is not set.
132 """
133 preprint_step = StepConfig(id="preprint", title=_("Init"))
134 metadata_step = StepConfig(id="metadata", title=_("Article data"))
135 info_step = StepConfig(id="info", title=_("Submission info"))
136 confirm_step = StepConfig(id="confirm", title=_("Confirm"))
138 preprint_step.can_navigate = True
139 preprint_step.href = reverse("mesh:submission_create")
141 if submission:
142 preprint_step.completed = True
143 preprint_step.href = reverse(
144 "mesh:submission_update_preprint", kwargs={"pk": submission.pk}
145 )
146 metadata_step.can_navigate = True
147 metadata_step.href = reverse(
148 "mesh:submission_edit_article_metadata", kwargs={"submission_pk": submission.pk}
149 )
151 if hasattr(submission.get_current_version(), "main_file"):
152 metadata_step.completed = True
153 info_step.can_navigate = True
154 info_step.href = reverse(
155 "mesh:submission_info_update", kwargs={"submission_pk": submission.pk}
156 )
158 if submission.author_agreement:
159 info_step.completed = True
160 confirm_step.can_navigate = True
161 confirm_step.href = reverse(
162 "mesh:submission_confirm", kwargs={"submission_pk": submission.pk}
163 )
165 return StepperConfig(
166 steps=[preprint_step, metadata_step, info_step, confirm_step],
167 title=_("Submit process"),
168 submission=submission,
169 )
172def add_stepper_button(
173 id, title, icon_class, name, href=None, btn_classes=[], data_attrs={}, submit_button=True
174):
175 attrs = {"name": [name]} | data_attrs
176 if href is None:
177 if submit_button:
178 attrs["type"] = ["submit"]
179 attrs["form"] = ["id-form-fullpage"]
180 btn_classes.append("save-button")
181 attrs["class"] = btn_classes
182 else:
183 attrs["href"] = [href]
184 btn_classes.extend(["as-button", "save-button"])
185 attrs["class"] = btn_classes
187 return Button(
188 id=id,
189 title=_(title),
190 icon_class=icon_class,
191 # attrs={"href": [author_step.href], "class": ["as-button"]},
192 attrs=attrs,
193 )