feat: add navigation bar with progress indicator and backward navigation
- Created _navigation.html component with 5-step progress indicator - Added German labels (E-Mail, Persönliche Daten, Motivation, Dokumente, Bestätigung) - Implemented can_access_page() validation in routes.py - Users can navigate backward to completed pages (data preserved) - Sequential forward progression enforced (cannot skip ahead) - Visual states: completed (green checkmark), current (blue), future (gray) - Added comprehensive CSS styling with responsive design - Updated base.html to include navigation component - Updated page templates to pass current_page context - Navigation appears on pages 2-5 only - All 102 tests passing with no regressions
This commit is contained in:
@@ -27,6 +27,25 @@ from app.utils import check_rate_limit
|
||||
def register_routes(app, mail):
|
||||
"""Register all application routes"""
|
||||
|
||||
def can_access_page(session_id, requested_page):
|
||||
"""
|
||||
Determine if user can access the requested page.
|
||||
|
||||
Rules:
|
||||
- Can always access pages <= current_page (backward navigation)
|
||||
- Cannot access pages > current_page (must progress sequentially)
|
||||
- Page 1 is always accessible
|
||||
"""
|
||||
if requested_page == 1:
|
||||
return True
|
||||
|
||||
app_data = load_application_data(session_id)
|
||||
if not app_data:
|
||||
return False
|
||||
|
||||
current_page = app_data.get('current_page', 1)
|
||||
return requested_page <= current_page
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""Redirect to apply page"""
|
||||
@@ -91,6 +110,11 @@ def register_routes(app, mail):
|
||||
@app.route('/apply/<session_id>/personal')
|
||||
def page2_personal(session_id):
|
||||
"""Page 2: Basic personal information"""
|
||||
# Validate page access
|
||||
if not can_access_page(session_id, 2):
|
||||
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
||||
return redirect(url_for('page1_email'))
|
||||
|
||||
app_data = load_application_data(session_id)
|
||||
if not app_data:
|
||||
flash('Bewerbung nicht gefunden.', 'error')
|
||||
@@ -99,6 +123,7 @@ def register_routes(app, mail):
|
||||
return render_template('page2_personal.html',
|
||||
session_id=session_id,
|
||||
job_name=app_data['job_name'],
|
||||
current_page=app_data.get('current_page', 2),
|
||||
data=app_data.get('personal_info', {}))
|
||||
|
||||
|
||||
@@ -180,6 +205,16 @@ def register_routes(app, mail):
|
||||
@app.route('/apply/<session_id>/motivation')
|
||||
def page3_motivation(session_id):
|
||||
"""Page 3: Motivation and qualification questions"""
|
||||
# Validate page access
|
||||
if not can_access_page(session_id, 3):
|
||||
app_data = load_application_data(session_id)
|
||||
if app_data:
|
||||
current_page = app_data.get('current_page', 2)
|
||||
if current_page == 2:
|
||||
return redirect(url_for('page2_personal', session_id=session_id))
|
||||
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
||||
return redirect(url_for('page1_email'))
|
||||
|
||||
app_data = load_application_data(session_id)
|
||||
if not app_data:
|
||||
flash('Bewerbung nicht gefunden.', 'error')
|
||||
@@ -188,6 +223,7 @@ def register_routes(app, mail):
|
||||
return render_template('page3_motivation.html',
|
||||
session_id=session_id,
|
||||
job_name=app_data['job_name'],
|
||||
current_page=app_data.get('current_page', 3),
|
||||
data=app_data.get('motivation_answers', {}))
|
||||
|
||||
|
||||
@@ -249,6 +285,18 @@ def register_routes(app, mail):
|
||||
@app.route('/apply/<session_id>/upload')
|
||||
def page4_upload(session_id):
|
||||
"""Page 4: Document upload"""
|
||||
# Validate page access
|
||||
if not can_access_page(session_id, 4):
|
||||
app_data = load_application_data(session_id)
|
||||
if app_data:
|
||||
current_page = app_data.get('current_page', 2)
|
||||
if current_page == 2:
|
||||
return redirect(url_for('page2_personal', session_id=session_id))
|
||||
elif current_page == 3:
|
||||
return redirect(url_for('page3_motivation', session_id=session_id))
|
||||
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
||||
return redirect(url_for('page1_email'))
|
||||
|
||||
app_data = load_application_data(session_id)
|
||||
if not app_data:
|
||||
flash('Bewerbung nicht gefunden.', 'error')
|
||||
@@ -257,6 +305,7 @@ def register_routes(app, mail):
|
||||
return render_template('page4_upload.html',
|
||||
session_id=session_id,
|
||||
job_name=app_data['job_name'],
|
||||
current_page=app_data.get('current_page', 4),
|
||||
uploaded_files=app_data.get('uploaded_files', []),
|
||||
max_files=app.config['MAX_FILES'])
|
||||
|
||||
@@ -395,6 +444,20 @@ def register_routes(app, mail):
|
||||
@app.route('/apply/<session_id>/confirmation')
|
||||
def page5_confirmation(session_id):
|
||||
"""Page 5: Confirmation page"""
|
||||
# Validate page access
|
||||
if not can_access_page(session_id, 5):
|
||||
app_data = load_application_data(session_id)
|
||||
if app_data:
|
||||
current_page = app_data.get('current_page', 2)
|
||||
if current_page == 2:
|
||||
return redirect(url_for('page2_personal', session_id=session_id))
|
||||
elif current_page == 3:
|
||||
return redirect(url_for('page3_motivation', session_id=session_id))
|
||||
elif current_page == 4:
|
||||
return redirect(url_for('page4_upload', session_id=session_id))
|
||||
flash('Sie müssen zuerst die vorherigen Schritte abschließen.', 'error')
|
||||
return redirect(url_for('page1_email'))
|
||||
|
||||
app_data = load_application_data(session_id)
|
||||
if not app_data:
|
||||
flash('Bewerbung nicht gefunden.', 'error')
|
||||
@@ -403,6 +466,7 @@ def register_routes(app, mail):
|
||||
return render_template('page5_confirmation.html',
|
||||
session_id=session_id,
|
||||
job_name=app_data['job_name'],
|
||||
current_page=app_data.get('current_page', 5),
|
||||
email=app_data['email'])
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user