Coverage for src/mesh/views/components/stepper.py: 26%

103 statements  

« prev     ^ index     » next       coverage.py v7.7.0, created at 2025-04-28 07:45 +0000

1from __future__ import annotations 

2 

3from dataclasses import dataclass, field 

4from typing import TYPE_CHECKING 

5 

6from django.urls import reverse_lazy 

7from django.utils.translation import gettext_lazy as _ 

8 

9from mesh.views.forms.base_forms import FormAction 

10 

11from .button import Button 

12 

13if TYPE_CHECKING: 13 ↛ 14line 13 didn't jump to line 14 because the condition on line 13 was never true

14 from ...models.submission_models import Submission 

15 

16 

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 the step config is navigable. 

29 # Usually it should be equals to `previous_step.completed` 

30 can_navigate: bool = field(default=False) 

31 # For some steps, the Next button is the form submit button 

32 # For others (use_href_for_next=True), the Next button is a simple link 

33 use_href_for_next_button: bool = field(default=False) 

34 

35 

36@dataclass 

37class StepperConfig: 

38 steps: list[StepConfig] = field(default_factory=list) 

39 title: str = field(default="") 

40 # Id of the active step 

41 active: str = field(default="") 

42 

43 def get_step(self, step_id: str) -> StepConfig | None: 

44 return next((s for s in self.steps if s.id == step_id), None) 

45 

46 def set_active_step(self, step_id: str): 

47 step = self.get_step(step_id) 

48 if step is None: 

49 return 

50 

51 for s in self.steps: 

52 s.active = False 

53 step.active = True 

54 self.active = step_id 

55 

56 def get_next_step(self, step_id): 

57 next_step = None 

58 i = 0 

59 while next_step is None and i < len(self.steps): 

60 if self.steps[i].id == step_id: 

61 if i + 1 < len(self.steps): 

62 next_step = self.steps[i + 1] 

63 i += 1 

64 return next_step 

65 

66 def get_previous_step(self, step_id): 

67 previous_step = None 

68 i = 0 

69 while previous_step is None and i < len(self.steps): 

70 if self.steps[i].id == step_id: 

71 if i - 1 > -1: 

72 previous_step = self.steps[i - 1] 

73 i += 1 

74 return previous_step 

75 

76 def set_completed_steps(self, step_ids: list[str]): 

77 for step in self.steps: 

78 step.completed = step.id in step_ids 

79 

80 def get_next_button(self): 

81 step_id = self.active 

82 button = None 

83 next_step = self.get_next_step(step_id) 

84 if next_step is not None: 

85 current_step = self.get_step(step_id) 

86 href = next_step.href if current_step.use_href_for_next_button else None 

87 button = add_stepper_button( 

88 id="next", 

89 title="Next", 

90 icon_class="fa-right-long", 

91 name=FormAction.NEXT.value, 

92 href=href, 

93 btn_classes=["button-highlight"], 

94 ) 

95 return button 

96 

97 def get_previous_button(self): 

98 step_id = self.active 

99 button = None 

100 previous_step = self.get_previous_step(step_id) 

101 if previous_step is not None: 

102 button = add_stepper_button( 

103 id="previous", 

104 title="Previous", 

105 icon_class="fa-left-long", 

106 name=FormAction.PREVIOUS.value, 

107 href=previous_step.href, 

108 ) 

109 return button 

110 

111 

112def get_submission_stepper(submission: Submission | None) -> StepperConfig: 

113 """ 

114 Returns the submission stepper config adapted with the given submission. 

115 The active step is not set. 

116 """ 

117 metadata_step = StepConfig(id="metadata", title=_("Article metadata")) 

118 authors_step = StepConfig( 

119 id="authors", 

120 title=_("Authors"), 

121 use_href_for_next_button=True, 

122 # description="I have something very very VERY long to say. Please make some space in the layout to render this description properly." 

123 ) 

124 version_step = StepConfig(id="version", title=_("Files")) 

125 confirm_step = StepConfig(id="confirm", title=_("Confirm")) 

126 

127 if submission: 

128 metadata_step.href = reverse_lazy("mesh:submission_update", kwargs={"pk": submission.pk}) 

129 metadata_step.completed = True 

130 metadata_step.can_navigate = True 

131 

132 authors_step.href = reverse_lazy("mesh:submission_authors", kwargs={"pk": submission.pk}) 

133 authors_step.completed = submission.authors.exists() # type:ignore 

134 authors_step.can_navigate = True 

135 

136 version = submission.current_version 

137 version_step.href = ( 

138 reverse_lazy("mesh:submission_version_update", kwargs={"pk": version.pk}) 

139 if version 

140 else reverse_lazy( 

141 "mesh:submission_version_create", 

142 kwargs={"submission_pk": submission.pk}, 

143 ) 

144 ) 

145 version_step.completed = version is not None 

146 version_step.can_navigate = authors_step.completed 

147 

148 if version: 

149 confirm_step.href = reverse_lazy( 

150 "mesh:submission_confirm", kwargs={"pk": submission.pk} 

151 ) 

152 confirm_step.can_navigate = True 

153 

154 return StepperConfig( 

155 steps=[metadata_step, authors_step, version_step, confirm_step], 

156 title=_("Submit process"), 

157 ) 

158 

159 

160def add_stepper_button(id, title, icon_class, name, href=None, btn_classes=[]): 

161 attrs = {"name": [name]} 

162 if href is None: 

163 attrs["type"] = ["submit"] 

164 attrs["form"] = ["id-form-fullpage"] 

165 btn_classes.append("save-button") 

166 attrs["class"] = btn_classes 

167 else: 

168 attrs["href"] = [href] 

169 btn_classes.extend(["as-button", "save-button"]) 

170 attrs["class"] = btn_classes 

171 

172 return Button( 

173 id=id, 

174 title=_(title), 

175 icon_class=icon_class, 

176 # attrs={"href": [author_step.href], "class": ["as-button"]}, 

177 attrs=attrs, 

178 )