Skip to content

Conversation

@Saksham-Sirohi
Copy link
Contributor

@Saksham-Sirohi Saksham-Sirohi commented Dec 11, 2025

Fixes #1391

Summary by Sourcery

Improve CFP submission flow to preserve user input and file selections across navigation, and enhance file/avatar handling UX.

New Features:

  • Allow CFP wizard steps to save partial form data and restore it when navigating back.
  • Provide UI controls and backend support for clearing previously uploaded files and avatars during CFP and profile flows.
  • Display names of previously uploaded files and currently selected files in file and avatar input widgets.

Bug Fixes:

  • Ensure CFP session data and uploaded files are refreshed on each request to avoid stale state when moving between steps.
  • Prevent loss of entered profile data when navigating back in the CFP wizard by prioritizing session-stored values over user model defaults.
  • Fix CFP event submission image clearing so that removing an image actually deletes it from the submission.

Enhancements:

  • Refine file upload widgets and templates to better reflect current, existing, and newly selected files, including improved filename display.
  • Simplify and standardize CFP file handling in session storage, including safer session mutation and optional file storage interactions.
  • Adjust avatar preview logic to correctly hide the existing avatar when it has been marked for clearing.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 11, 2025

Reviewer's Guide

Refactors the CfP submission flow to better preserve form and file state across navigation, adds explicit handling for clearing previously uploaded files (including avatars), and updates templates/widgets/JS to show current file names and support client-side file selection UI.

Sequence diagram for CfP back navigation with partial data and file preservation

sequenceDiagram
    actor User
    participant Browser
    participant CfpWizardView as CfpWizardView
    participant FormFlowStep as FormFlowStep
    participant Session as DjangoSession

    User->>Browser: Click Back button
    Browser->>CfpWizardView: POST action=back
    CfpWizardView->>CfpWizardView: dispatch(request)
    CfpWizardView->>FormFlowStep: get_step(request)
    note over CfpWizardView,FormFlowStep: request.method == POST and action == back
    CfpWizardView-->>FormFlowStep: result (step response)

    FormFlowStep->>FormFlowStep: post(request)
    FormFlowStep->>FormFlowStep: get_form()
    FormFlowStep->>FormFlowStep: _save_partial_data(form)
    FormFlowStep->>Session: update cfp_session[data][step]
    FormFlowStep->>FormFlowStep: set_files(form.files)
    FormFlowStep->>Session: update cfp_session[files][step]
    FormFlowStep->>FormFlowStep: get_prev_url(request)
    FormFlowStep-->>Browser: redirect(prev_url) or render current step

    Browser->>CfpWizardView: GET prev_url
    CfpWizardView->>FormFlowStep: step.get(request)
    FormFlowStep->>FormFlowStep: get_form_initial()
    FormFlowStep->>Session: read cfp_session initial/data/files
    FormFlowStep-->>Browser: Render form with restored fields and file names
Loading

Sequence diagram for clearing saved CfP files and avatars

sequenceDiagram
    actor User
    participant Browser
    participant FormFlowStep as FormFlowStep
    participant ProfileView as ProfileStepView
    participant FileStorage as FileSystemStorage
    participant Session as DjangoSession
    participant DB as Database

    rect rgb(235, 245, 255)
    note over User,Browser: Clear file in CfP step (session-based upload)
    User->>Browser: Click Clear on saved file
    Browser->>FormFlowStep: POST clear_file=field_name
    FormFlowStep->>FormFlowStep: post(request)
    FormFlowStep->>FormFlowStep: _clear_file(field_name)
    FormFlowStep->>Session: read cfp_session[files][step][field_name]
    alt tmp_name present
        FormFlowStep->>FileStorage: delete(tmp_name)
    end
    FormFlowStep->>Session: remove files[step][field_name]
    FormFlowStep->>Session: append field_name to clear_files[step]
    FormFlowStep->>Session: mark request.session.modified
    FormFlowStep-->>Browser: get(request) -> re-render step
    end

    rect rgb(235, 255, 235)
    note over User,Browser: Clear avatar in profile step (DB file)
    User->>Browser: Click Clear avatar
    Browser->>ProfileView: POST clear_file=avatar
    ProfileView->>ProfileView: post(request)
    ProfileView->>DB: load user
    ProfileView->>FileStorage: user.avatar.delete(save=True)
    ProfileView->>DB: set user.avatar=None and save
    ProfileView-->>Browser: redirect to same path
    Browser->>ProfileView: GET profile page
    ProfileView->>Session: read clear_files flags
    ProfileView-->>Browser: Render form with avatar cleared
    end
Loading

Updated class diagram for CfP flow steps and file widgets

classDiagram
    class FlowStepBase {
        +request
        +identifier
        +get_next_applicable(request)
        +get_prev_url(request)
        +get_next_url(request)
        +get_step_url(request, query)
        +is_applicable(request)
        +is_completed(request)
        +cfp_session
    }

    class FormFlowStep {
        +file_storage
        +get_form_initial()
        +get_saved_file_objects()
        +get_form(from_storage)
        +post(request)
        +_clear_file(field_name)
        +_save_partial_data(form)
        +set_data(data)
        +get_files()
        +set_files(files)
    }

    class SavedFileWrapper {
        +is_saved_file
        +name
        +__init__(name)
        +__str__()
        +__bool__()
    }

    class BasenameFileInput {
        +FakeFile
        +get_context(name, value, attrs)
    }

    class FakeFile {
        +file
        +name
        +__init__(file)
        +__str__()
        +url
    }

    class ImageInput {
        +get_context(name, value, attrs)
    }

    class ProfileForm {
        +user
        +user_fields
        +__init__(*args, name, **kwargs)
    }

    FlowStepBase <|-- FormFlowStep
    BasenameFileInput <|-- ImageInput
    BasenameFileInput *-- FakeFile
    FormFlowStep o-- SavedFileWrapper
    ProfileForm o-- FormFlowStep
Loading

File-Level Changes

Change Details Files
Make CfP session access dynamic and ensure URLs and form initial data are built from fresh session state.
  • Change cfp_session from cached_property to regular property so each access reflects current request/session state.
  • Copy resolver kwargs when building step URLs to avoid mutating request resolver_match.kwargs.
  • Refactor FormFlowStep.get_form_initial to merge initial and previous data from session once, then augment with saved file wrapper objects.
app/eventyay/cfp/flow.py
Introduce SavedFileWrapper and enhance form/file handling to preserve and clear uploaded files across steps and back navigation.
  • Add SavedFileWrapper class to represent already-saved files in forms while remaining truthy and printable by filename.
  • Extend FormFlowStep.get_form_initial/get_form to inject SavedFileWrapper instances and strip them from POST data when re-binding forms from storage.
  • Implement _clear_file helper to remove session-stored uploads or mark DB-backed files for clearing via session clear_files flags.
  • Add _save_partial_data to store partially valid form data (excluding uploaded files) for back navigation, and update set_data/set_files to work off mutable session_data and mark the session as modified.
  • Update get_files to reconstruct UploadedFile instances from session and set_files to no-op on empty input and to clear any prior clear_files flags when a new file is uploaded.
app/eventyay/cfp/flow.py
Wire back-navigation and clear-file actions into the CfP wizard flow and submission template.
  • Change the submission_base back link into a POST submit button with action=back, so server can differentiate navigation intent.
  • In FormFlowStep.post, inspect action and clear_file to either clear files, save partial data + files, and redirect to previous step, or proceed with normal validation and forward navigation.
  • Update wizard dispatch to short-circuit processing when POST includes back or clear_file, returning the current step’s response directly.
app/eventyay/cfp/flow.py
app/eventyay/cfp/templates/cfp/event/submission_base.html
app/eventyay/cfp/views/wizard.py
Revamp file and image widgets/templates to display current filenames, support clearing, and integrate a shared JS controller.
  • Adjust FakeFile.str to return the full filename (including extension) instead of just the stem.
  • Update ClearableBasenameFileInput.get_context to skip wrapping SavedFileWrapper values, and extend ImageInput.get_context to annotate widget context with is_saved_file and saved_filename for different value types.
  • Rewrite image_input.html to handle three states: saved file from session, existing file with preview and clear checkbox, and newly selected file; include new markup for showing selected filenames and clear buttons and load a shared fileInput.js script.
  • Add fileInput.js implementing handleFileSelect, clearFileInput, and clearExistingFile helpers and auto-binding to .form-file-selected elements on DOMContentLoaded.
app/eventyay/common/forms/widgets.py
app/eventyay/common/templates/common/widgets/image_input.html
app/eventyay/static/common/js/fileInput.js
Enhance avatar/profile handling to respect session data, support clearing avatars, and use the new file input UX.
  • Change ProfileForm initialization to let session-derived initial values override user model fields, improving back navigation behavior.
  • Switch avatar widget from ClearableBasenameFileInput to a plain FileInput, letting the template/JS drive the UX instead of the old clearable widget behavior.
  • Extend profile step get_form_kwargs/get_context to pull the saved fullname from cfp_session.data and to surface saved avatar file info and clear_flags to the template.
  • Update avatar.html to show currently selected avatar from session or user, hide avatar preview when marked cleared, and include selected-file UI plus fileInput.js.
  • In profile done(), delete the user’s avatar from storage if it was marked for clearing in the session.
  • Add logic in CfP submission view (user.py) to process clear_file=image, deleting the submission image and redirecting back to the same page.
app/eventyay/person/forms/profile.py
app/eventyay/cfp/flow.py
app/eventyay/common/templates/common/avatar.html
app/eventyay/cfp/views/user.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1391 Preserve CfP proposal form state (including text fields and uploads) when navigating between steps, especially when going back from the “Profile” step.
#1391 Ensure the CfP “Back” button performs step navigation only and does not reset or clear the proposal form data.
#1391 Persist and correctly restore uploaded files and their UI state across forward/back navigation and explicit file clearing actions in the CfP flow.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The session update logic is duplicated and slightly inconsistent across _clear_file, _save_partial_data, set_data, and set_files (e.g. repeatedly doing session_data = self.cfp_session and then self.request.session['cfp'] = self.request.session.get('cfp', {})); consider centralizing this into a small helper and avoid what appears to be a no-op reassignment that could become problematic if cfp_session ever returns a copy instead of a live dict.
  • The functions in fileInput.js (e.g. handleFileSelect) assume that elements like name + '-selected-info' and name + '-selected-name' always exist, which will cause runtime errors if the script is used with inputs that don't render the matching markup; add null checks or guard clauses before dereferencing these elements.
  • fileInput.js is included both from the image input widget and from avatar.html, so it may be loaded multiple times on the same page; consider loading it once via the base layout or the asset pipeline to avoid duplicate global definitions.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The session update logic is duplicated and slightly inconsistent across `_clear_file`, `_save_partial_data`, `set_data`, and `set_files` (e.g. repeatedly doing `session_data = self.cfp_session` and then `self.request.session['cfp'] = self.request.session.get('cfp', {})`); consider centralizing this into a small helper and avoid what appears to be a no-op reassignment that could become problematic if `cfp_session` ever returns a copy instead of a live dict.
- The functions in `fileInput.js` (e.g. `handleFileSelect`) assume that elements like `name + '-selected-info'` and `name + '-selected-name'` always exist, which will cause runtime errors if the script is used with inputs that don't render the matching markup; add null checks or guard clauses before dereferencing these elements.
- `fileInput.js` is included both from the image input widget and from `avatar.html`, so it may be loaded multiple times on the same page; consider loading it once via the base layout or the asset pipeline to avoid duplicate global definitions.

## Individual Comments

### Comment 1
<location> `app/eventyay/common/templates/common/widgets/image_input.html:28` </location>
<code_context>
 {% compress js %}
     <script defer src="{% static "cfp/js/profile.js" %}"></script>
 {% endcompress %}
+<script src="{% static 'common/js/fileInput.js' %}"></script>
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Including `fileInput.js` inside the widget template can lead to multiple redundant script loads and event bindings.

Since this template is rendered once per file field, each instance will inject another `fileInput.js` `<script>` tag. The browser may only fetch it once, but it will execute on every inclusion, registering duplicate listeners and handlers. Please move the script tag to a layout/base template (or another once-per-page block) and keep this widget responsible only for its HTML markup.

Suggested implementation:

```
<div class="form-file-selected mb-2" id="{{ widget.name }}-selected-info" style="display: none;">
    <span class="text-muted">{% translate "Selected" %}: </span>
    <strong id="{{ widget.name }}-selected-name"></strong>
    <button type="button" class="btn btn-sm btn-outline-danger ml-2" onclick="clearFileInput('{{ widget.name }}')">{% translate "Clear" %}</button>
</div>
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} onchange="handleFileSelect(this)">

```

To fully implement your review comment, you’ll also need to:
1. Include `fileInput.js` once per page, e.g. in a base or layout template in the appropriate JS block:
   - For example, in a base template:  
     `{% block extra_js %}`  
     `    <script src="{% static 'common/js/fileInput.js' %}"></script>`  
     `{% endblock %}`
2. Ensure that any pages using this widget extend that base template or otherwise pull in `fileInput.js` so `handleFileSelect` and `clearFileInput` are available.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements CFP submission flow preservation to maintain user input and file selections when navigating between wizard steps, addressing issue #1391. The implementation adds partial form data persistence, file upload handling with session storage, and UI controls for clearing previously uploaded files and avatars.

  • Converts back navigation from anchor links to form submissions to enable data preservation
  • Adds JavaScript-driven file selection UI with clear functionality across forms
  • Implements session-based file storage for CFP wizard with SavedFileWrapper for display

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
app/eventyay/static/common/js/fileInput.js New JavaScript module for handling file input interactions and display state management
app/eventyay/person/forms/profile.py Updates profile form initialization to prioritize session data over user model defaults for back navigation
app/eventyay/common/templates/common/widgets/image_input.html Redesigns image input widget with file selection status display and clear buttons
app/eventyay/common/templates/common/avatar.html Adds avatar file selection UI with saved file display and clearing functionality
app/eventyay/common/forms/widgets.py Extends ImageInput widget context to support SavedFileWrapper and display filenames
app/eventyay/cfp/views/wizard.py Adds early return handling for back navigation and file clearing actions
app/eventyay/cfp/views/user.py Implements submission image clearing via clear_file POST parameter
app/eventyay/cfp/templates/cfp/event/submission_base.html Changes back button from link to submit button for form data preservation
app/eventyay/cfp/flow.py Major refactoring: converts cfp_session to uncached property, adds partial data saving, file clearing logic, and SavedFileWrapper class for session-stored files
Comments suppressed due to low confidence (1)

app/eventyay/cfp/flow.py:311

  • 'except' clause does nothing but pass and there is no explanatory comment.
                except Exception:

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Saksham-Sirohi Saksham-Sirohi force-pushed the cfp-flow-movement-preservation branch from 1ce62b8 to 3a66db7 Compare December 11, 2025 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Back button erases CfP proposal progress

1 participant