from flask_wtf import FlaskForm from wtforms import IntegerField, SelectField, StringField, SubmitField, TextAreaField from wtforms.fields import DateTimeLocalField from wtforms.validators import Length, NumberRange, Optional, ValidationError from .models import TASK_PRIORITIES, TASK_STATUSES STATUS_LABELS = { "not_started": "Not started", "in_progress": "In progress", "done": "Done", } PRIORITY_LABELS = { "urgent": "Urgent", "important": "Important", "normal": "Normal", } def not_blank(_form: FlaskForm, field: StringField) -> None: if not field.data or not field.data.strip(): raise ValidationError("This field cannot be blank.") class TaskForm(FlaskForm): title = StringField( "Task title", validators=[not_blank, Length(max=200)], ) description = TextAreaField( "Notes", validators=[Optional(), Length(max=5000)], ) status = SelectField( "Status", choices=[(value, STATUS_LABELS[value]) for value in TASK_STATUSES], default="not_started", ) priority = SelectField( "Priority", choices=[(value, PRIORITY_LABELS[value]) for value in TASK_PRIORITIES], default="normal", ) due_date = DateTimeLocalField( "Due date and time", format="%Y-%m-%dT%H:%M", validators=[Optional()], ) submit = SubmitField("Save task") class SubtaskForm(FlaskForm): title = StringField( "Next small step", validators=[not_blank, Length(max=100)], ) submit = SubmitField("Add step") class ActionForm(FlaskForm): submit = SubmitField() def valid_chunk_plan(_form: FlaskForm, field: StringField) -> None: if not field.data or not field.data.strip(): return values = [value.strip() for value in field.data.split(",")] if len(values) > 12: raise ValidationError("Use at most 12 focus blocks.") try: minutes = [int(value) for value in values] except ValueError as exc: raise ValidationError( "Use comma-separated whole minutes, such as 5, 5, 10." ) from exc if any(value < 1 or value > 120 for value in minutes): raise ValidationError("Each focus block must be 1 to 120 minutes.") class FocusSettingsForm(FlaskForm): timer_minutes = IntegerField( "Countdown length in minutes", validators=[Optional(), NumberRange(min=1, max=180)], ) context = StringField( "Helpful context", validators=[Optional(), Length(max=100)], ) chunk_plan = StringField( "Focus blocks", validators=[Optional(), Length(max=120), valid_chunk_plan], ) submit = SubmitField("Save focus setup") def parsed_chunks(self) -> list[int]: if not self.chunk_plan.data or not self.chunk_plan.data.strip(): return [] return [ int(value.strip()) for value in self.chunk_plan.data.split(",") ]