import json from nicegui import ui # --- Survey Options --- FREQUENCY_OPTIONS = ["Daily", "Weekly", "Monthly", "Rarely"] FEATURE_OPTIONS = ["News Feed", "Marketplace", "Groups", "Messenger", "Stories"] OTHER_APP_OPTIONS = ["Instagram", "TikTok", "X/Twitter", "LinkedIn", "Snapchat", "Reddit"] GENDER_OPTIONS = ["Male", "Female", "Non-binary", "Prefer not to say"] class SurveyState: def __init__(self): self.frequency = None self.selected_features = [] self.feature_feedback = {} self.age = None self.gender = None self.location = "" def save_to_json(self): data = { "usage": {"frequency": self.frequency, "features_used": self.selected_features}, "feature_feedback": self.feature_feedback, "demographics": {"age": self.age, "gender": self.gender, "location": self.location}, } with open("survey_results.json", "w") as f: json.dump(data, f, indent=4) state = SurveyState() def create_survey(): container = ui.column().classes("w-full items-center q-pa-md") # --- PAGE 1: USAGE --- def show_page_1(): container.clear() with container: ui.label("Facebook Usage").classes("text-h4 mb-4") with ui.card().classes("w-full max-w-lg p-6"): ui.label("How frequently do you use Facebook?").classes("text-bold") p1_radio = ui.radio(options=FREQUENCY_OPTIONS).bind_value(state, "frequency") ui.label("What features do you use?").classes("text-bold mt-4") p1_select = ( ui.select(options=FEATURE_OPTIONS, multiple=True) .classes("w-full") .bind_value(state, "selected_features") ) next_btn = ui.button("Next", on_click=show_page_2).classes("mt-6 w-full") # Validation ui.timer( 0.1, lambda: next_btn.set_enabled(state.frequency is not None and len(state.selected_features) > 0) ) # --- PAGE 2: STAR RATINGS & CONDITIONAL FEEDBACK --- def show_page_2(): container.clear() # Initialize dictionary for selected features for f in state.selected_features: if f not in state.feature_feedback: state.feature_feedback[f] = {"rating": 5, "likes": "", "other_apps": [], "improvements": ""} with container: ui.label("Feature Feedback").classes("text-h4 mb-4") # We define the button and validation logic first to ensure they are in scope def is_page_2_valid(): for f in state.selected_features: data = state.feature_feedback[f] if data["rating"] < 4: if len(data.get("improvements", "")) < 20: return False return True for feature in state.selected_features: with ui.card().classes("w-full max-w-lg p-6 mb-4"): ui.label(f"Rate the {feature}").classes("text-h6 text-primary") # Star Rating (Quasar based) stars = ( ui.rating(value=5, icon="star") .classes("text-3xl") .bind_value(state.feature_feedback[feature], "rating") ) # CASE: Rating 4 or 5 with ui.column().classes("w-full").bind_visibility_from(stars, "value", backward=lambda v: v >= 4): ui.textarea("What do you like about this feature?").classes("w-full").bind_value( state.feature_feedback[feature], "likes" ) # CASE: Rating below 4 with ui.column().classes("w-full").bind_visibility_from(stars, "value", backward=lambda v: v < 4): ui.label("What other apps do you use for this?") ui.select(options=OTHER_APP_OPTIONS, multiple=True).classes("w-full").bind_value( state.feature_feedback[feature], "other_apps" ) ui.textarea("How can this be improved? (min 20 characters)").classes("w-full").bind_value( state.feature_feedback[feature], "improvements" ) with ui.row().classes("w-full max-w-lg justify-between mt-4"): ui.button("Back", on_click=show_page_1).props("outline") next_btn = ui.button("Next", on_click=show_page_3) ui.tooltip("Please answer all questions (min 20 chars for improvements)").bind_visibility_from( next_btn, "enabled", backward=lambda v: not v ) # Reactive validation timer ui.timer(0.5, lambda: next_btn.set_enabled(is_page_2_valid())) # --- PAGE 3: DEMOGRAPHICS --- def show_page_3(): container.clear() with container: ui.label("Demographics").classes("text-h4 mb-4") with ui.card().classes("w-full max-w-lg p-6"): ui.number("Age", format="%d").classes("w-full").bind_value(state, "age") ui.select(label="Gender", options=GENDER_OPTIONS).classes("w-full").bind_value(state, "gender") ui.input("Location").classes("w-full").bind_value(state, "location") with ui.row().classes("w-full justify-between mt-6"): ui.button("Back", on_click=show_page_2).props("outline") ui.button("Review Survey", on_click=show_confirmation).classes("bg-blue") # --- PAGE 4: CONFIRMATION (SUMMARY) --- def show_confirmation(): container.clear() with container: ui.label("Review Your Answers").classes("text-h4 mb-4") with ui.card().classes("w-full max-w-2xl p-6"): with ui.column().classes("w-full gap-2"): ui.label(f"**Frequency:** {state.frequency}").classes("text-md") ui.label(f"**Features:** {', '.join(state.selected_features)}") ui.separator().classes("my-2") for f in state.selected_features: d = state.feature_feedback[f] ui.label(f"**{f}**: {d['rating']} Stars").classes("text-primary") if d["rating"] >= 4: ui.label(f"Likes: {d['likes'] or 'No comment'}").classes("ml-4 text-grey-8") else: ui.label(f"Improvements: {d['improvements']}").classes("ml-4 text-grey-8") ui.separator().classes("my-2") ui.label( f"**Age:** {state.age or 'N/A'} | **Gender:** {state.gender} | **Location:** {state.location}" ) with ui.row().classes("w-full justify-between mt-8"): ui.button("Back to Edit", on_click=show_page_3).props("outline") ui.button("Confirm & Submit", on_click=handle_final_submit).classes("bg-green") # --- FINAL PAGE: ACKNOWLEDGMENT --- def handle_final_submit(): state.save_to_json() container.clear() with container: with ui.card().classes("w-full max-w-lg p-12 items-center text-center"): ui.icon("verified", color="green").classes("text-6xl") ui.label("Submission Successful").classes("text-h4 mt-2") ui.label("Thank you for your time. Your responses have been saved.").classes("text-grey-7") # No buttons here prevents the user from going back show_page_1() create_survey() ui.run()