Files
wizarr/app/forms/wizard.py
engels74 d0df4c6d5d feat: wizard step refactor into pre/post wizard steps
Changes:
  - Pre/post invitation wizard phases
  - Phase-aware routing (/pre-wizard/*, /post-wizard/*)
  - Phase indicator badges (visual categorization)
  - Dynamic completion button behavior
  - Two-column admin layout with category drag-and-drop
  - Invite code management service
  - Enhanced wizard export/import
  - Comprehensive test coverage (17 new test files)
2025-10-16 15:28:48 +02:00

132 lines
3.9 KiB
Python

from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed, FileField, FileRequired
from wtforms import BooleanField, HiddenField, SelectField, StringField, TextAreaField
from wtforms.validators import DataRequired, Optional
class WizardStepForm(FlaskForm):
server_type = SelectField(
"Server Type",
choices=[
("plex", "Plex"),
("jellyfin", "Jellyfin"),
("emby", "Emby"),
("audiobookshelf", "Audiobookshelf"),
("romm", "Romm"),
("komga", "Komga"),
("kavita", "Kavita"),
],
validators=[DataRequired()],
)
category = SelectField(
"Category",
choices=[
("pre_invite", "Before Invite Acceptance"),
("post_invite", "After Invite Acceptance"),
],
default="post_invite",
validators=[DataRequired()],
)
position = HiddenField("Position", default="0")
title = StringField("Title", validators=[Optional()])
markdown = TextAreaField("Markdown", validators=[DataRequired()])
# Require explicit user interaction before enabling Next
require_interaction = BooleanField(
"Require User Interaction",
default=False,
description="Block the user continuing, until they click a button or link in this step.",
)
class WizardPresetForm(FlaskForm):
"""Form for creating wizard steps from presets."""
server_type = SelectField(
"Server Type",
choices=[
("plex", "Plex"),
("jellyfin", "Jellyfin"),
("emby", "Emby"),
("audiobookshelf", "Audiobookshelf"),
("romm", "Romm"),
("komga", "Komga"),
("kavita", "Kavita"),
],
validators=[DataRequired()],
)
category = SelectField(
"Category",
choices=[
("pre_invite", "Before Invite Acceptance"),
("post_invite", "After Invite Acceptance"),
],
default="post_invite",
validators=[DataRequired()],
)
preset_id = SelectField(
"Preset",
choices=[], # Will be populated dynamically
validators=[DataRequired()],
)
# Variables for preset templates
discord_id = StringField("Discord Server ID", validators=[Optional()])
overseerr_url = StringField("Overseerr/Ombi URL", validators=[Optional()])
class WizardBundleForm(FlaskForm):
"""Simple form to create / edit a WizardBundle."""
name = StringField("Name", validators=[DataRequired()])
description = StringField("Description", validators=[Optional()])
# optional: Steps selection handled in separate UI; keep form minimal
class SimpleWizardStepForm(FlaskForm):
"""Minimal form for bundle-only steps (no server_type, no requires)."""
category = SelectField(
"Category",
choices=[
("pre_invite", "Before Invite Acceptance"),
("post_invite", "After Invite Acceptance"),
],
default="post_invite",
validators=[DataRequired()],
)
title = StringField("Title", validators=[Optional()])
markdown = TextAreaField("Markdown", validators=[DataRequired()])
# Allow interaction requirement for custom/bundle steps as well
require_interaction = BooleanField(
"Require User Interaction",
default=False,
description="Block the user continuing, until they click a button or link in this step.",
)
class WizardImportForm(FlaskForm):
"""Form for importing wizard steps or bundles from JSON files."""
file = FileField(
"JSON File",
validators=[
FileRequired("Please select a JSON file to import."),
FileAllowed(["json"], "Only JSON files are allowed."),
],
)
replace_existing = BooleanField(
"Replace Existing",
default=False,
description="Check to replace existing data, leave unchecked to merge with existing.",
)