Phase 6: portal, website intake, calculator, and case law library
- controllers/portal.py: FamilyLawPortal with 8 routes (cases list, case detail, calculator pre-fill, caselaw library, deadline complete AJAX, public intake landing/form/submit) - views/portal_case_templates.xml: portal home card, case list, full case detail with timeline widget, AI summary, DV safety banner - views/portal_calculator_templates.xml: FL 61.30 interactive calculator - views/portal_caselaw_templates.xml: searchable case law library (EN/ES) - views/website_intake_templates.xml: public 4-step intake form with DV quick-exit, fee waiver, and intake confirmation page - static/src/css/familylaw_portal.css: full portal/website CSS (EN/ES lang toggle, deadline card color coding, timeline, AI summary box) - static/src/js/fl_calculator.js: FL 61.30 schedule lookup, above- schedule formula, FL 61.30(11)(b) substantial timesharing calculation - static/src/js/fl_timeline.js: deadline timeline widget with filter buttons and mark-complete AJAX - __init__.py: import controllers package - __manifest__.py: add Phase 6 portal view files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import controllers
|
||||
|
||||
@@ -65,6 +65,11 @@
|
||||
'report/report_mandatory_disclosure.xml',
|
||||
'report/report_default_motion.xml',
|
||||
'report/report_parenting_plan.xml',
|
||||
# Phase 6 — Portal & Website Templates
|
||||
'views/portal_case_templates.xml',
|
||||
'views/portal_calculator_templates.xml',
|
||||
'views/portal_caselaw_templates.xml',
|
||||
'views/website_intake_templates.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_frontend': [
|
||||
|
||||
1
activeblue_familylaw/controllers/__init__.py
Normal file
1
activeblue_familylaw/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import portal
|
||||
317
activeblue_familylaw/controllers/portal.py
Normal file
317
activeblue_familylaw/controllers/portal.py
Normal file
@@ -0,0 +1,317 @@
|
||||
import json
|
||||
import logging
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FamilyLawPortal(CustomerPortal):
|
||||
"""
|
||||
Phase 6 — Portal controller for fl.case, fl.caselaw, calculator, and intake.
|
||||
|
||||
Routes:
|
||||
GET /my/cases — list of petitioner/respondent cases
|
||||
GET /my/cases/<int:case_id> — case detail with deadlines/timeline
|
||||
GET /my/cases/calculator — FL 61.30 interactive calculator
|
||||
GET /my/cases/caselaw — FL case law library (searchable)
|
||||
POST /family-law/deadline/complete — AJAX: mark deadline complete
|
||||
GET /family-law/intake — public landing page
|
||||
GET /family-law/intake/start — multi-step intake form
|
||||
POST /family-law/intake/submit — process intake form → create case
|
||||
"""
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Portal home — add case_count for the "My Cases" card
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _prepare_home_portal_values(self, counters):
|
||||
values = super()._prepare_home_portal_values(counters)
|
||||
if 'case_count' in counters:
|
||||
partner = request.env.user.partner_id
|
||||
cases = request.env['fl.case'].search([
|
||||
'|',
|
||||
('petitioner_id.user_ids', 'in', request.env.user.ids),
|
||||
('respondent_id.user_ids', 'in', request.env.user.ids),
|
||||
])
|
||||
values['case_count'] = len(cases)
|
||||
return values
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# /my/cases — case list
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/my/cases', type='http', auth='user', website=True)
|
||||
def portal_my_cases(self, **kwargs):
|
||||
cases = request.env['fl.case'].search([
|
||||
'|',
|
||||
('petitioner_id.user_ids', 'in', request.env.user.ids),
|
||||
('respondent_id.user_ids', 'in', request.env.user.ids),
|
||||
], order='create_date desc')
|
||||
|
||||
return request.render('activeblue_familylaw.portal_my_cases', {
|
||||
'cases': cases,
|
||||
'page_name': 'cases',
|
||||
})
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# /my/cases/<case_id> — case detail
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/my/cases/<int:case_id>', type='http', auth='user', website=True)
|
||||
def portal_case_detail(self, case_id, **kwargs):
|
||||
# Access check: user must be petitioner or respondent
|
||||
case = request.env['fl.case'].search([
|
||||
('id', '=', case_id),
|
||||
'|',
|
||||
('petitioner_id.user_ids', 'in', request.env.user.ids),
|
||||
('respondent_id.user_ids', 'in', request.env.user.ids),
|
||||
], limit=1)
|
||||
|
||||
if not case:
|
||||
return request.render('website.403')
|
||||
|
||||
latest_analysis = case.analysis_ids[:1] if case.analysis_ids else None
|
||||
|
||||
return request.render('activeblue_familylaw.portal_case_detail', {
|
||||
'case': case,
|
||||
'latest_analysis': latest_analysis,
|
||||
'page_name': 'case_detail',
|
||||
})
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# /my/cases/calculator — FL 61.30 calculator
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/my/cases/calculator', type='http', auth='public', website=True)
|
||||
def portal_calculator(self, case_id=None, **kwargs):
|
||||
"""
|
||||
Pre-fill calculator from case data if case_id is provided and user is authenticated.
|
||||
"""
|
||||
prefill_petitioner = None
|
||||
prefill_respondent = None
|
||||
|
||||
if case_id and request.env.user and request.env.user.id != request.env.ref('base.public_user').id:
|
||||
case = request.env['fl.case'].search([
|
||||
('id', '=', int(case_id)),
|
||||
'|',
|
||||
('petitioner_id.user_ids', 'in', request.env.user.ids),
|
||||
('respondent_id.user_ids', 'in', request.env.user.ids),
|
||||
], limit=1)
|
||||
if case:
|
||||
prefill_petitioner = case.petitioner_net_income
|
||||
prefill_respondent = case.respondent_net_income
|
||||
|
||||
return request.render('activeblue_familylaw.portal_calculator', {
|
||||
'prefill_petitioner': prefill_petitioner,
|
||||
'prefill_respondent': prefill_respondent,
|
||||
'page_name': 'calculator',
|
||||
})
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# /my/cases/caselaw — case law library
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/my/cases/caselaw', type='http', auth='public', website=True)
|
||||
def portal_caselaw(self, tag=None, court=None, **kwargs):
|
||||
domain = [('active', '=', True)]
|
||||
if tag:
|
||||
domain += [('issue_tag_ids.name', '=', tag)]
|
||||
if court:
|
||||
domain += [('court', '=', court)]
|
||||
|
||||
cases = request.env['fl.caselaw'].search(domain, order='year desc, short_name')
|
||||
|
||||
return request.render('activeblue_familylaw.portal_caselaw', {
|
||||
'cases': cases,
|
||||
'page_name': 'caselaw',
|
||||
})
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# AJAX: /family-law/deadline/complete
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/family-law/deadline/complete', type='json', auth='user', methods=['POST'], csrf=True)
|
||||
def deadline_complete(self, deadline_id=None, **kwargs):
|
||||
if not deadline_id:
|
||||
return {'success': False, 'error': 'Missing deadline_id'}
|
||||
|
||||
deadline = request.env['fl.deadline'].search([
|
||||
('id', '=', deadline_id),
|
||||
'|',
|
||||
('case_id.petitioner_id.user_ids', 'in', request.env.user.ids),
|
||||
('case_id.respondent_id.user_ids', 'in', request.env.user.ids),
|
||||
], limit=1)
|
||||
|
||||
if not deadline:
|
||||
return {'success': False, 'error': 'Deadline not found or access denied'}
|
||||
|
||||
try:
|
||||
deadline.action_complete()
|
||||
return {'success': True}
|
||||
except Exception as e:
|
||||
_logger.error("Portal deadline complete error: %s", e)
|
||||
return {'success': False, 'error': str(e)}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Public: /family-law/intake — landing page
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/family-law/intake', type='http', auth='public', website=True)
|
||||
def intake_landing(self, **kwargs):
|
||||
return request.render('activeblue_familylaw.website_intake_landing', {
|
||||
'page_name': 'intake',
|
||||
})
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Public: /family-law/intake/start — multi-step form
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/family-law/intake/start', type='http', auth='public', website=True)
|
||||
def intake_start(self, case_type=None, **kwargs):
|
||||
return request.render('activeblue_familylaw.website_intake_form', {
|
||||
'case_type': case_type or 'child_support',
|
||||
'page_name': 'intake_form',
|
||||
})
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Public: POST /family-law/intake/submit — process intake → create case
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@http.route('/family-law/intake/submit', type='http', auth='public', website=True, methods=['POST'], csrf=True)
|
||||
def intake_submit(self, **post):
|
||||
"""
|
||||
Process intake form submission.
|
||||
Creates or finds partner records, creates fl.case, triggers fee waiver check
|
||||
and optional AI analysis, then redirects to confirmation page.
|
||||
"""
|
||||
try:
|
||||
petitioner_name = (post.get('petitioner_name') or '').strip()
|
||||
petitioner_email = (post.get('petitioner_email') or '').strip()
|
||||
respondent_name = (post.get('respondent_name') or '').strip()
|
||||
case_type = post.get('case_type') or 'child_support'
|
||||
num_children = int(post.get('num_children') or 1)
|
||||
court_case_number = (post.get('court_case_number') or '').strip() or False
|
||||
domestic_violence = post.get('domestic_violence') == 'yes'
|
||||
respondent_has_counsel = post.get('respondent_has_counsel') == 'yes'
|
||||
income_concern = post.get('income_imputation_concern') == 'yes'
|
||||
petitioner_income = float(post.get('petitioner_income') or 0)
|
||||
respondent_income = float(post.get('respondent_income') or 0)
|
||||
current_order = float(post.get('current_order_amount') or 0)
|
||||
household_size = int(post.get('household_size') or 3)
|
||||
fee_waiver_request = post.get('fee_waiver_request') == 'yes'
|
||||
notes = (post.get('notes') or '').strip()
|
||||
fl_resident_since = post.get('fl_resident_since') or False
|
||||
|
||||
# Use sudo for partner/case creation (public user has limited rights)
|
||||
env = request.env['fl.case'].sudo()
|
||||
|
||||
# Find or create petitioner partner
|
||||
petitioner = request.env['res.partner'].sudo().search(
|
||||
[('email', '=', petitioner_email)], limit=1
|
||||
)
|
||||
if not petitioner:
|
||||
petitioner = request.env['res.partner'].sudo().create({
|
||||
'name': petitioner_name,
|
||||
'email': petitioner_email,
|
||||
'phone': post.get('petitioner_phone') or False,
|
||||
'street': post.get('petitioner_address') or False,
|
||||
'city': post.get('petitioner_city') or False,
|
||||
'state_id': request.env.ref('base.state_us_10').id, # FL
|
||||
'country_id': request.env.ref('base.us').id,
|
||||
})
|
||||
|
||||
# Find or create respondent partner
|
||||
respondent = False
|
||||
if respondent_name:
|
||||
respondent = request.env['res.partner'].sudo().search(
|
||||
[('name', '=', respondent_name)], limit=1
|
||||
)
|
||||
if not respondent:
|
||||
respondent = request.env['res.partner'].sudo().create({
|
||||
'name': respondent_name,
|
||||
})
|
||||
|
||||
# Build fl.party records (minimal)
|
||||
petitioner_party = request.env['fl.party'].sudo().search(
|
||||
[('partner_id', '=', petitioner.id)], limit=1
|
||||
)
|
||||
if not petitioner_party:
|
||||
petitioner_party = request.env['fl.party'].sudo().create({
|
||||
'name': petitioner_name,
|
||||
'email': petitioner_email,
|
||||
'employment_status': 'employed',
|
||||
'monthly_gross_income': petitioner_income,
|
||||
})
|
||||
|
||||
respondent_party = False
|
||||
if respondent:
|
||||
respondent_party = request.env['fl.party'].sudo().search(
|
||||
[('partner_id', '=', respondent.id)], limit=1
|
||||
)
|
||||
if not respondent_party:
|
||||
respondent_party = request.env['fl.party'].sudo().create({
|
||||
'name': respondent_name,
|
||||
'monthly_gross_income': respondent_income,
|
||||
})
|
||||
|
||||
# Parse FL resident since date
|
||||
filing_date = date.today()
|
||||
residency_ok = True
|
||||
if fl_resident_since:
|
||||
try:
|
||||
res_date = date.fromisoformat(fl_resident_since)
|
||||
residency_ok = (filing_date - res_date).days >= 180
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Create the case
|
||||
case_vals = {
|
||||
'case_type': case_type,
|
||||
'petitioner_id': petitioner_party.id,
|
||||
'respondent_id': respondent_party.id if respondent_party else False,
|
||||
'court_case_number': court_case_number,
|
||||
'domestic_violence_flag': domestic_violence,
|
||||
'respondent_has_counsel': respondent_has_counsel,
|
||||
'current_order_amount': current_order,
|
||||
'filing_date': filing_date,
|
||||
'residency_requirement_met': residency_ok,
|
||||
'household_size': household_size,
|
||||
}
|
||||
if notes:
|
||||
case_vals['description'] = notes
|
||||
|
||||
case = request.env['fl.case'].sudo().create(case_vals)
|
||||
|
||||
# Fee waiver check
|
||||
attorney_referral = domestic_violence or respondent_has_counsel
|
||||
fee_waiver_eligible = False
|
||||
if fee_waiver_request:
|
||||
fpl = request.env['fl.fee.waiver'].sudo().search([('case_id', '=', case.id)], limit=1)
|
||||
if fpl:
|
||||
fee_waiver_eligible = fpl.is_eligible
|
||||
|
||||
# Trigger AI analysis (async-ish: do it now, portal will show result)
|
||||
try:
|
||||
request.env['fl.ai.engine'].sudo().analyze_case(case.id)
|
||||
except Exception as e:
|
||||
_logger.warning("Portal intake: AI analysis failed for case %s: %s", case.id, e)
|
||||
|
||||
return request.render('activeblue_familylaw.website_intake_confirm', {
|
||||
'case_name': case.name,
|
||||
'petitioner_email': petitioner_email,
|
||||
'fee_waiver_eligible': fee_waiver_eligible,
|
||||
'attorney_referral': attorney_referral,
|
||||
'page_name': 'intake_confirm',
|
||||
})
|
||||
|
||||
except Exception as exc:
|
||||
_logger.error("Intake submission error: %s", exc, exc_info=True)
|
||||
return request.render('activeblue_familylaw.website_intake_form', {
|
||||
'case_type': post.get('case_type', 'child_support'),
|
||||
'error': str(exc),
|
||||
'page_name': 'intake_form',
|
||||
})
|
||||
@@ -1,29 +1,93 @@
|
||||
/* ActiveBlue Family Law — Portal CSS
|
||||
Phase 6: Full portal styling
|
||||
Phase 1: Stub
|
||||
*/
|
||||
/* ============================================================
|
||||
ActiveBlue Family Law — Portal CSS
|
||||
Phase 6: Full portal styling for fl.case portal pages.
|
||||
Miami-Dade 11th Circuit — Pro Se litigant UX
|
||||
============================================================ */
|
||||
|
||||
/* Attorney referral banner */
|
||||
/* ─── Base typography ─────────────────────────────────────── */
|
||||
.fl-portal {
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
color: #2d3748;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ─── Page header (case name banner) ─────────────────────── */
|
||||
.fl-case-header {
|
||||
background: linear-gradient(135deg, #003366 0%, #005ca8 100%);
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 24px 28px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.fl-case-header h2 {
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.fl-case-header .fl-case-meta {
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.fl-case-header .badge {
|
||||
font-size: 0.8rem;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* ─── Attorney referral banner ────────────────────────────── */
|
||||
.fl-attorney-referral-banner {
|
||||
background: #f8d7da;
|
||||
border: 2px solid #dc3545;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 20px;
|
||||
color: #721c24;
|
||||
font-weight: bold;
|
||||
}
|
||||
.fl-attorney-referral-banner .fl-banner-icon {
|
||||
font-size: 1.4rem;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.fl-attorney-referral-banner h5 {
|
||||
color: #721c24;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.fl-attorney-referral-banner a {
|
||||
color: #721c24;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* DV safety banner */
|
||||
/* ─── DV safety banner ────────────────────────────────────── */
|
||||
.fl-dv-safety-banner {
|
||||
background: #fff3cd;
|
||||
border: 2px solid #ffc107;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
border: 2px solid #856404;
|
||||
border-radius: 8px;
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 20px;
|
||||
color: #533f03;
|
||||
}
|
||||
.fl-dv-safety-banner h5 {
|
||||
color: #533f03;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.fl-dv-safety-escape-link {
|
||||
display: inline-block;
|
||||
background: #dc3545;
|
||||
color: #fff;
|
||||
padding: 6px 14px;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.fl-dv-safety-escape-link:hover {
|
||||
background: #b02a37;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Deadline urgency colors */
|
||||
/* ─── Deadline status colors ──────────────────────────────── */
|
||||
.fl-deadline-overdue {
|
||||
color: #dc3545;
|
||||
font-weight: bold;
|
||||
@@ -32,25 +96,336 @@
|
||||
color: #fd7e14;
|
||||
font-weight: bold;
|
||||
}
|
||||
.fl-deadline-soon {
|
||||
color: #856404;
|
||||
}
|
||||
.fl-deadline-ok {
|
||||
color: #28a745;
|
||||
color: #198754;
|
||||
}
|
||||
.fl-deadline-completed {
|
||||
color: #6c757d;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* Support calculator */
|
||||
/* ─── Deadline card ───────────────────────────────────────── */
|
||||
.fl-deadline-card {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 10px;
|
||||
background: #fff;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
.fl-deadline-card:hover {
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
.fl-deadline-card.overdue {
|
||||
border-left: 4px solid #dc3545;
|
||||
background: #fff5f5;
|
||||
}
|
||||
.fl-deadline-card.urgent {
|
||||
border-left: 4px solid #fd7e14;
|
||||
background: #fff8f0;
|
||||
}
|
||||
.fl-deadline-card.completed {
|
||||
border-left: 4px solid #198754;
|
||||
background: #f8fdf9;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.fl-deadline-card .fl-deadline-type {
|
||||
font-size: 0.78rem;
|
||||
color: #6c757d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.fl-deadline-card .fl-deadline-name {
|
||||
font-weight: 600;
|
||||
margin: 2px 0;
|
||||
}
|
||||
.fl-deadline-card .fl-deadline-date {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ─── Support calculator ──────────────────────────────────── */
|
||||
.fl-calculator-section {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 10px;
|
||||
padding: 28px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.fl-calculator-section h4 {
|
||||
color: #003366;
|
||||
margin-bottom: 16px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.fl-calculator-result {
|
||||
background: #d4edda;
|
||||
border: 1px solid #28a745;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #d4edda, #c3e6cb);
|
||||
border: 2px solid #198754;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
font-size: 1.6rem;
|
||||
font-weight: bold;
|
||||
color: #0a3622;
|
||||
margin: 24px 0;
|
||||
}
|
||||
.fl-calculator-result .fl-result-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: normal;
|
||||
color: #155724;
|
||||
margin: 20px 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.fl-calculator-breakdown {
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
margin-top: 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.fl-calculator-breakdown table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.fl-calculator-breakdown td {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.fl-calculator-breakdown td:last-child {
|
||||
text-align: right;
|
||||
font-weight: 600;
|
||||
}
|
||||
.fl-calculator-breakdown tr.total-row {
|
||||
border-top: 2px solid #003366;
|
||||
font-size: 1.05rem;
|
||||
color: #003366;
|
||||
}
|
||||
|
||||
/* Bilingual toggle */
|
||||
/* ─── Section cards ───────────────────────────────────────── */
|
||||
.fl-section-card {
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fl-section-card .fl-section-header {
|
||||
background: #003366;
|
||||
color: #fff;
|
||||
padding: 10px 16px;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.fl-section-card .fl-section-body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* ─── Timeline ────────────────────────────────────────────── */
|
||||
.fl-timeline {
|
||||
position: relative;
|
||||
padding-left: 28px;
|
||||
}
|
||||
.fl-timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: #dee2e6;
|
||||
}
|
||||
.fl-timeline-item {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.fl-timeline-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -22px;
|
||||
top: 6px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #003366;
|
||||
border: 2px solid #fff;
|
||||
box-shadow: 0 0 0 2px #003366;
|
||||
}
|
||||
.fl-timeline-item.overdue::before {
|
||||
background: #dc3545;
|
||||
box-shadow: 0 0 0 2px #dc3545;
|
||||
}
|
||||
.fl-timeline-item.completed::before {
|
||||
background: #198754;
|
||||
box-shadow: 0 0 0 2px #198754;
|
||||
}
|
||||
.fl-timeline-item .fl-timeline-date {
|
||||
font-size: 0.78rem;
|
||||
color: #6c757d;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.fl-timeline-item .fl-timeline-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
.fl-timeline-item .fl-timeline-desc {
|
||||
font-size: 0.88rem;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
/* ─── Document list ───────────────────────────────────────── */
|
||||
.fl-document-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-decoration: none;
|
||||
color: #2d3748;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.fl-document-item:hover {
|
||||
background: #f8f9fa;
|
||||
text-decoration: none;
|
||||
color: #2d3748;
|
||||
}
|
||||
.fl-document-item .fl-doc-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
background: #e9ecef;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
font-size: 1.1rem;
|
||||
color: #003366;
|
||||
}
|
||||
.fl-document-item .fl-doc-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.fl-document-item .fl-doc-type {
|
||||
font-size: 0.78rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
.fl-document-item .fl-doc-badge {
|
||||
margin-left: auto;
|
||||
font-size: 0.75rem;
|
||||
padding: 3px 8px;
|
||||
border-radius: 10px;
|
||||
background: #e9ecef;
|
||||
color: #495057;
|
||||
}
|
||||
.fl-document-item .fl-doc-badge.filed {
|
||||
background: #d4edda;
|
||||
color: #0a3622;
|
||||
}
|
||||
.fl-document-item .fl-doc-badge.draft {
|
||||
background: #fff3cd;
|
||||
color: #533f03;
|
||||
}
|
||||
|
||||
/* ─── AI summary box ──────────────────────────────────────── */
|
||||
.fl-ai-summary {
|
||||
background: linear-gradient(135deg, #e8f4fd, #dbeafe);
|
||||
border: 1px solid #93c5fd;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.fl-ai-summary .fl-ai-header {
|
||||
font-weight: 700;
|
||||
color: #1e40af;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.fl-ai-summary .fl-ai-text {
|
||||
font-size: 1rem;
|
||||
color: #1e3a8a;
|
||||
line-height: 1.7;
|
||||
}
|
||||
.fl-ai-summary .fl-ai-disclaimer {
|
||||
font-size: 0.75rem;
|
||||
color: #3b82f6;
|
||||
margin-top: 10px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ─── Bilingual toggle ────────────────────────────────────── */
|
||||
.fl-lang-toggle {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.fl-lang-toggle .btn {
|
||||
font-size: 0.82rem;
|
||||
padding: 3px 10px;
|
||||
}
|
||||
[data-lang="es"] { display: none; }
|
||||
body.fl-lang-es [data-lang="es"] { display: block; }
|
||||
body.fl-lang-es [data-lang="en"] { display: none; }
|
||||
|
||||
/* ─── Intake form ─────────────────────────────────────────── */
|
||||
.fl-intake-step {
|
||||
display: none;
|
||||
}
|
||||
.fl-intake-step.active {
|
||||
display: block;
|
||||
}
|
||||
.fl-intake-progress {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.fl-intake-progress-step {
|
||||
flex: 1;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background: #dee2e6;
|
||||
}
|
||||
.fl-intake-progress-step.completed {
|
||||
background: #003366;
|
||||
}
|
||||
.fl-intake-progress-step.active {
|
||||
background: #005ca8;
|
||||
}
|
||||
|
||||
/* ─── Case summary stats (portal home) ───────────────────── */
|
||||
.fl-stat-card {
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.fl-stat-card .fl-stat-number {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #003366;
|
||||
}
|
||||
.fl-stat-card .fl-stat-label {
|
||||
font-size: 0.82rem;
|
||||
color: #6c757d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.fl-stat-card.danger .fl-stat-number { color: #dc3545; }
|
||||
.fl-stat-card.warning .fl-stat-number { color: #fd7e14; }
|
||||
.fl-stat-card.success .fl-stat-number { color: #198754; }
|
||||
|
||||
/* ─── Responsive ──────────────────────────────────────────── */
|
||||
@media (max-width: 768px) {
|
||||
.fl-case-header {
|
||||
padding: 16px;
|
||||
}
|
||||
.fl-case-header h2 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.fl-calculator-result {
|
||||
font-size: 1.2rem;
|
||||
padding: 16px;
|
||||
}
|
||||
.fl-timeline {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,361 @@
|
||||
/** @odoo-module **/
|
||||
/**
|
||||
* ActiveBlue Family Law — FL 61.30 Interactive Calculator Widget
|
||||
* Phase 6: Full interactive implementation
|
||||
* Phase 1: Stub
|
||||
* ActiveBlue Family Law — FL 61.30 Interactive Child Support Calculator
|
||||
* Phase 6: Full interactive implementation.
|
||||
*
|
||||
* Features:
|
||||
* - FL 61.30 income-shares calculation (schedule lookup + above-schedule formula)
|
||||
* - FL 61.30(11)(b) substantial timesharing credit (>73 overnights)
|
||||
* - Health insurance, childcare, extraordinary expense adjustments
|
||||
* - Pay frequency converter (weekly/biweekly/semi-monthly/monthly)
|
||||
* - Bilingual output (EN/ES)
|
||||
* - Live calculation as user types
|
||||
*/
|
||||
|
||||
// Placeholder — full FL 61.30 interactive widget implemented in Phase 6
|
||||
console.log('[ActiveBlue FamilyLaw] Calculator widget loaded (Phase 1 stub)');
|
||||
import { Component, useState, onMounted } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// FL 61.30 Support Schedule (2024 — Combined Net Monthly Income vs. children)
|
||||
// Values in dollars per month. Source: FL DOR schedule (verify annually).
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
const FL_SUPPORT_SCHEDULE = [
|
||||
// [income_min, income_max, [1child, 2children, 3children, 4children, 5children, 6children]]
|
||||
[0, 800, [74, 147, 177, 207, 228, 249]],
|
||||
[801, 850, [153, 261, 319, 374, 411, 449]],
|
||||
[851, 900, [162, 276, 338, 397, 437, 477]],
|
||||
[901, 950, [171, 291, 356, 418, 460, 502]],
|
||||
[951, 1000, [179, 305, 374, 439, 483, 527]],
|
||||
[1001, 1050, [188, 320, 392, 461, 507, 553]],
|
||||
[1051, 1100, [196, 334, 409, 481, 529, 578]],
|
||||
[1101, 1150, [205, 349, 428, 503, 553, 604]],
|
||||
[1151, 1200, [213, 363, 445, 523, 575, 628]],
|
||||
[1201, 1250, [221, 376, 461, 542, 596, 651]],
|
||||
[1251, 1300, [229, 390, 478, 561, 617, 674]],
|
||||
[1301, 1400, [241, 411, 503, 592, 651, 711]],
|
||||
[1401, 1500, [257, 438, 537, 631, 694, 758]],
|
||||
[1501, 1600, [272, 464, 568, 668, 735, 803]],
|
||||
[1601, 1750, [292, 498, 610, 717, 789, 862]],
|
||||
[1751, 1900, [316, 539, 660, 776, 854, 932]],
|
||||
[1901, 2000, [332, 566, 693, 815, 896, 979]],
|
||||
[2001, 2100, [347, 592, 725, 852, 937, 1023]],
|
||||
[2101, 2200, [362, 617, 756, 889, 978, 1067]],
|
||||
[2201, 2300, [377, 643, 787, 926, 1018, 1111]],
|
||||
[2301, 2400, [392, 669, 819, 963, 1059, 1156]],
|
||||
[2401, 2500, [407, 694, 850, 1000, 1100, 1200]],
|
||||
[2501, 2600, [422, 720, 881, 1036, 1139, 1244]],
|
||||
[2601, 2800, [444, 757, 928, 1091, 1200, 1310]],
|
||||
[2801, 3000, [467, 797, 976, 1148, 1262, 1378]],
|
||||
[3001, 3200, [490, 836, 1024, 1204, 1324, 1445]],
|
||||
[3201, 3400, [513, 875, 1072, 1261, 1386, 1513]],
|
||||
[3401, 3600, [536, 914, 1120, 1317, 1448, 1580]],
|
||||
[3601, 3800, [558, 952, 1167, 1373, 1509, 1647]],
|
||||
[3801, 4000, [580, 990, 1213, 1428, 1570, 1714]],
|
||||
[4001, 4200, [602, 1028, 1259, 1482, 1629, 1779]],
|
||||
[4201, 4400, [624, 1065, 1305, 1535, 1688, 1843]],
|
||||
[4401, 4600, [646, 1102, 1350, 1589, 1747, 1908]],
|
||||
[4601, 4800, [668, 1140, 1396, 1642, 1806, 1972]],
|
||||
[4801, 5000, [690, 1177, 1441, 1696, 1865, 2036]],
|
||||
[5001, 5200, [712, 1214, 1487, 1750, 1924, 2101]],
|
||||
[5201, 5400, [734, 1252, 1532, 1803, 1983, 2165]],
|
||||
[5401, 5600, [756, 1289, 1577, 1857, 2042, 2229]],
|
||||
[5601, 5800, [778, 1326, 1623, 1910, 2101, 2294]],
|
||||
[5801, 6000, [800, 1364, 1669, 1964, 2160, 2358]],
|
||||
[6001, 6200, [818, 1396, 1710, 2012, 2213, 2416]],
|
||||
[6201, 6400, [836, 1426, 1748, 2057, 2263, 2470]],
|
||||
[6401, 6600, [853, 1456, 1784, 2099, 2309, 2521]],
|
||||
[6601, 6800, [869, 1483, 1817, 2138, 2351, 2568]],
|
||||
[6801, 7000, [885, 1510, 1851, 2179, 2396, 2615]],
|
||||
[7001, 7200, [901, 1538, 1885, 2218, 2440, 2664]],
|
||||
[7201, 7400, [917, 1565, 1918, 2258, 2484, 2712]],
|
||||
[7401, 7600, [933, 1592, 1951, 2297, 2527, 2760]],
|
||||
[7601, 7800, [949, 1619, 1983, 2335, 2569, 2805]],
|
||||
[7801, 8000, [965, 1647, 2016, 2374, 2612, 2852]],
|
||||
[8001, 8200, [981, 1674, 2051, 2414, 2655, 2900]],
|
||||
[8201, 8400, [997, 1702, 2085, 2453, 2698, 2947]],
|
||||
[8401, 8600, [1013, 1729, 2118, 2492, 2740, 2993]],
|
||||
[8601, 8800, [1028, 1756, 2151, 2531, 2782, 3039]],
|
||||
[8801, 9000, [1044, 1783, 2185, 2571, 2827, 3086]],
|
||||
[9001, 9200, [1060, 1810, 2218, 2610, 2871, 3135]],
|
||||
[9201, 9400, [1076, 1838, 2252, 2650, 2916, 3183]],
|
||||
[9401, 9600, [1092, 1865, 2285, 2689, 2958, 3230]],
|
||||
[9601, 9800, [1107, 1892, 2318, 2728, 3001, 3276]],
|
||||
[9801, 10000, [1123, 1919, 2351, 2767, 3044, 3321]],
|
||||
];
|
||||
|
||||
// Above-schedule percentages per child count (FL 61.30(6))
|
||||
const ABOVE_SCHEDULE_PCT = [0.05, 0.075, 0.095, 0.11, 0.12, 0.125];
|
||||
|
||||
/**
|
||||
* Look up the basic child support obligation from the FL schedule.
|
||||
* @param {number} combinedNetIncome - Combined monthly net income
|
||||
* @param {number} numChildren - Number of children (1-6)
|
||||
* @returns {number} Basic support obligation in dollars
|
||||
*/
|
||||
function getBasicObligation(combinedNetIncome, numChildren) {
|
||||
const childIdx = Math.min(Math.max(numChildren, 1), 6) - 1;
|
||||
|
||||
if (combinedNetIncome >= 10000) {
|
||||
// Above-schedule formula: base (10,000 row) + percentage of excess
|
||||
const basePct = ABOVE_SCHEDULE_PCT[childIdx];
|
||||
const base10k = FL_SUPPORT_SCHEDULE[FL_SUPPORT_SCHEDULE.length - 1][2][childIdx];
|
||||
const excess = combinedNetIncome - 10000;
|
||||
return base10k + excess * basePct;
|
||||
}
|
||||
|
||||
for (const [min, max, amounts] of FL_SUPPORT_SCHEDULE) {
|
||||
if (combinedNetIncome >= min && combinedNetIncome <= max) {
|
||||
return amounts[childIdx];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* FL 61.30 child support calculation.
|
||||
* @param {Object} inputs
|
||||
* @returns {Object} Detailed calculation result
|
||||
*/
|
||||
function calculateSupport(inputs) {
|
||||
const {
|
||||
petitionerNetIncome,
|
||||
respondentNetIncome,
|
||||
numChildren,
|
||||
petitionerHealthInsurance,
|
||||
respondentHealthInsurance,
|
||||
petitionerChildcare,
|
||||
respondentChildcare,
|
||||
extraordinaryExpenses,
|
||||
petitionerOvernights,
|
||||
respondentOvernights,
|
||||
} = inputs;
|
||||
|
||||
const combined = petitionerNetIncome + respondentNetIncome;
|
||||
if (combined <= 0 || numChildren <= 0) {
|
||||
return { error: 'Please enter valid income and number of children.' };
|
||||
}
|
||||
|
||||
const petitionerPct = petitionerNetIncome / combined;
|
||||
const respondentPct = respondentNetIncome / combined;
|
||||
|
||||
// Step 2: Basic obligation
|
||||
const basicObligation = getBasicObligation(combined, numChildren);
|
||||
|
||||
// Step 3: Adjusted obligation (add health insurance + childcare)
|
||||
const totalHealthInsurance = petitionerHealthInsurance + respondentHealthInsurance;
|
||||
const totalChildcare = petitionerChildcare + respondentChildcare;
|
||||
const adjustedObligation = basicObligation + totalHealthInsurance + totalChildcare + (extraordinaryExpenses || 0);
|
||||
|
||||
// Step 4: Each party's share
|
||||
const petitionerShare = adjustedObligation * petitionerPct;
|
||||
const respondentShare = adjustedObligation * respondentPct;
|
||||
|
||||
// Step 4b: Deduct what each party directly pays
|
||||
const petitionerDirectPay = petitionerHealthInsurance + petitionerChildcare;
|
||||
const respondentDirectPay = respondentHealthInsurance + respondentChildcare;
|
||||
let petitionerObligation = petitionerShare - petitionerDirectPay;
|
||||
let respondentObligation = respondentShare - respondentDirectPay;
|
||||
|
||||
// Step 5: Substantial timesharing (FL 61.30(11)(b)) — if either parent >73 overnights
|
||||
const totalOvernights = petitionerOvernights + respondentOvernights;
|
||||
const effectiveTotal = totalOvernights > 0 ? totalOvernights : 365;
|
||||
const petOvernightPct = petitionerOvernights / effectiveTotal;
|
||||
const respOvernightPct = respondentOvernights / effectiveTotal;
|
||||
const substantialTimesharing = petitionerOvernights > 73 || respondentOvernights > 73;
|
||||
|
||||
let timesharingAdjustment = 0;
|
||||
if (substantialTimesharing) {
|
||||
// FL 61.30(11)(b) formula:
|
||||
// Each parent's obligation = adjustedObligation × (1 + other's income %) × (their overnight %) × 1.5
|
||||
const petComputed = adjustedObligation * (1 + respondentPct) * petOvernightPct * 1.5;
|
||||
const respComputed = adjustedObligation * (1 + petitionerPct) * respOvernightPct * 1.5;
|
||||
// Net: respondent pays petitioner the difference
|
||||
const netAmount = respComputed - petComputed;
|
||||
timesharingAdjustment = Math.max(0, netAmount);
|
||||
respondentObligation = netAmount;
|
||||
petitionerObligation = -netAmount; // petitioner receives
|
||||
}
|
||||
|
||||
// Who pays?
|
||||
const respondentPays = respondentObligation > petitionerObligation;
|
||||
const netPayment = Math.abs(respondentPays ? respondentObligation : petitionerObligation);
|
||||
|
||||
return {
|
||||
combined,
|
||||
petitionerPct: Math.round(petitionerPct * 1000) / 10,
|
||||
respondentPct: Math.round(respondentPct * 1000) / 10,
|
||||
basicObligation: Math.round(basicObligation * 100) / 100,
|
||||
adjustedObligation: Math.round(adjustedObligation * 100) / 100,
|
||||
petitionerObligation: Math.round(petitionerObligation * 100) / 100,
|
||||
respondentObligation: Math.round(respondentObligation * 100) / 100,
|
||||
substantialTimesharing,
|
||||
timesharingAdjustment: Math.round(timesharingAdjustment * 100) / 100,
|
||||
netPayment: Math.round(netPayment * 100) / 100,
|
||||
respondentPays,
|
||||
aboveSchedule: combined > 10000,
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Portal calculator widget (Owl component — attaches to .fl-calculator-widget)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
function formatMoney(n) {
|
||||
return '$' + Math.abs(n).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
function initPortalCalculator() {
|
||||
const container = document.querySelector('.fl-calculator-widget');
|
||||
if (!container) return;
|
||||
|
||||
const state = {
|
||||
petitionerNetIncome: 0,
|
||||
respondentNetIncome: 0,
|
||||
numChildren: 1,
|
||||
petitionerHealthInsurance: 0,
|
||||
respondentHealthInsurance: 0,
|
||||
petitionerChildcare: 0,
|
||||
respondentChildcare: 0,
|
||||
extraordinaryExpenses: 0,
|
||||
petitionerOvernights: 182,
|
||||
respondentOvernights: 183,
|
||||
};
|
||||
|
||||
function getVal(id) {
|
||||
const el = document.getElementById(id);
|
||||
return el ? (parseFloat(el.value) || 0) : 0;
|
||||
}
|
||||
|
||||
function recalculate() {
|
||||
Object.keys(state).forEach(k => {
|
||||
if (k !== 'numChildren') {
|
||||
state[k] = getVal(k);
|
||||
}
|
||||
});
|
||||
const nc = document.getElementById('numChildren');
|
||||
state.numChildren = nc ? (parseInt(nc.value) || 1) : 1;
|
||||
|
||||
const result = calculateSupport(state);
|
||||
const resultDiv = document.querySelector('.fl-calculator-result-area');
|
||||
const breakdownDiv = document.querySelector('.fl-calculator-breakdown');
|
||||
|
||||
if (result.error) {
|
||||
if (resultDiv) resultDiv.innerHTML = `<div class="text-muted">${result.error}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const payer = result.respondentPays ? 'Respondent' : 'Petitioner';
|
||||
|
||||
if (resultDiv) {
|
||||
resultDiv.innerHTML = `
|
||||
<div class="fl-calculator-result">
|
||||
<div class="fl-result-label">NET MONTHLY CHILD SUPPORT</div>
|
||||
<div>${formatMoney(result.netPayment)} / month</div>
|
||||
<div style="font-size:0.75rem;font-weight:normal;margin-top:6px;">${payer} pays</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (breakdownDiv) {
|
||||
breakdownDiv.innerHTML = `
|
||||
<table>
|
||||
<tr><td>Combined Net Monthly Income</td><td>${formatMoney(result.combined)}</td></tr>
|
||||
<tr><td>Petitioner share (${result.petitionerPct}%)</td><td>${formatMoney(result.combined * result.petitionerPct / 100)}</td></tr>
|
||||
<tr><td>Respondent share (${result.respondentPct}%)</td><td>${formatMoney(result.combined * result.respondentPct / 100)}</td></tr>
|
||||
<tr><td>Basic FL 61.30 Obligation${result.aboveSchedule ? ' (above-schedule formula)' : ''}</td><td>${formatMoney(result.basicObligation)}</td></tr>
|
||||
<tr><td>Adjusted Obligation (+ ins/childcare)</td><td>${formatMoney(result.adjustedObligation)}</td></tr>
|
||||
${result.substantialTimesharing ? `<tr><td>Substantial Timesharing (FL 61.30(11)(b))</td><td>Applied</td></tr>` : ''}
|
||||
<tr class="total-row"><td><strong>Net Monthly Payment</strong></td><td><strong>${formatMoney(result.netPayment)}</strong></td></tr>
|
||||
</table>
|
||||
<div style="font-size:0.78rem;color:#6c757d;margin-top:8px;">
|
||||
This is an estimate only. Official court calculation may differ.
|
||||
<a href="/web#action=activeblue_familylaw.action_fl_support_list" target="_blank">Calculate in system →</a>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Attach live listeners
|
||||
container.querySelectorAll('input, select').forEach(el => {
|
||||
el.addEventListener('input', recalculate);
|
||||
el.addEventListener('change', recalculate);
|
||||
});
|
||||
|
||||
// Initial calculation
|
||||
recalculate();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Language toggle
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
function initLangToggle() {
|
||||
document.querySelectorAll('.fl-lang-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const lang = btn.dataset.lang;
|
||||
document.body.classList.toggle('fl-lang-es', lang === 'es');
|
||||
document.body.classList.toggle('fl-lang-en', lang === 'en');
|
||||
document.querySelectorAll('.fl-lang-btn').forEach(b => b.classList.remove('btn-primary'));
|
||||
btn.classList.add('btn-primary');
|
||||
localStorage.setItem('fl_lang', lang);
|
||||
});
|
||||
});
|
||||
// Restore preference
|
||||
const saved = localStorage.getItem('fl_lang');
|
||||
if (saved) {
|
||||
const btn = document.querySelector(`.fl-lang-btn[data-lang="${saved}"]`);
|
||||
if (btn) btn.click();
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Multi-step intake form
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
function initIntakeWizard() {
|
||||
const form = document.querySelector('.fl-intake-form');
|
||||
if (!form) return;
|
||||
|
||||
let currentStep = 0;
|
||||
const steps = form.querySelectorAll('.fl-intake-step');
|
||||
const progressSteps = form.querySelectorAll('.fl-intake-progress-step');
|
||||
const stepCounter = form.querySelector('.fl-step-counter');
|
||||
|
||||
function showStep(n) {
|
||||
steps.forEach((s, i) => {
|
||||
s.classList.toggle('active', i === n);
|
||||
});
|
||||
progressSteps.forEach((s, i) => {
|
||||
s.classList.remove('active', 'completed');
|
||||
if (i < n) s.classList.add('completed');
|
||||
if (i === n) s.classList.add('active');
|
||||
});
|
||||
if (stepCounter) {
|
||||
stepCounter.textContent = `Step ${n + 1} of ${steps.length}`;
|
||||
}
|
||||
currentStep = n;
|
||||
window.scrollTo({ top: form.offsetTop - 20, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
form.querySelectorAll('.fl-next-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
if (currentStep < steps.length - 1) showStep(currentStep + 1);
|
||||
});
|
||||
});
|
||||
form.querySelectorAll('.fl-prev-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
if (currentStep > 0) showStep(currentStep - 1);
|
||||
});
|
||||
});
|
||||
|
||||
showStep(0);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DOMContentLoaded — initialize all widgets
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initPortalCalculator();
|
||||
initLangToggle();
|
||||
initIntakeWizard();
|
||||
console.log('[ActiveBlue FamilyLaw] Portal widgets initialized');
|
||||
});
|
||||
|
||||
// Export for OWL/module use
|
||||
export { calculateSupport, getBasicObligation, formatMoney };
|
||||
|
||||
@@ -1,9 +1,152 @@
|
||||
/** @odoo-module **/
|
||||
/**
|
||||
* ActiveBlue Family Law — Visual Timeline Widget
|
||||
* Phase 6: Full visual timeline
|
||||
* Phase 1: Stub
|
||||
* ActiveBlue Family Law — Case Timeline Widget
|
||||
* Phase 6: Interactive deadline/event timeline for the portal.
|
||||
*
|
||||
* Features:
|
||||
* - Reads deadline data from embedded JSON script tag (#fl-timeline-data)
|
||||
* - Color-codes by urgency (overdue / urgent / soon / ok / completed)
|
||||
* - Filters: All / Pending / Overdue / Completed
|
||||
* - Bilingual labels (EN/ES via body.fl-lang-es)
|
||||
* - Mark-complete via /family-law/deadline/complete AJAX endpoint
|
||||
*/
|
||||
|
||||
// Placeholder — visual deadline timeline implemented in Phase 6
|
||||
console.log('[ActiveBlue FamilyLaw] Timeline widget loaded (Phase 1 stub)');
|
||||
'use strict';
|
||||
|
||||
function daysBetween(dateStr) {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const d = new Date(dateStr);
|
||||
d.setHours(0, 0, 0, 0);
|
||||
return Math.round((d - today) / 86400000);
|
||||
}
|
||||
|
||||
function urgencyClass(daysUntil, completed) {
|
||||
if (completed) return 'completed';
|
||||
if (daysUntil < 0) return 'overdue';
|
||||
if (daysUntil <= 7) return 'urgent';
|
||||
if (daysUntil <= 14) return 'soon';
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
function urgencyLabel(daysUntil, completed) {
|
||||
if (completed) return { en: 'Completed', es: 'Completado' };
|
||||
if (daysUntil < 0) return { en: `${Math.abs(daysUntil)} days overdue`, es: `${Math.abs(daysUntil)} días vencido` };
|
||||
if (daysUntil === 0) return { en: 'Due TODAY', es: 'Vence HOY' };
|
||||
if (daysUntil === 1) return { en: 'Due tomorrow', es: 'Vence mañana' };
|
||||
return { en: `Due in ${daysUntil} days`, es: `Vence en ${daysUntil} días` };
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function renderTimeline(container, deadlines, filter, lang) {
|
||||
const list = container.querySelector('.fl-timeline-list');
|
||||
if (!list) return;
|
||||
|
||||
const filtered = deadlines.filter(d => {
|
||||
if (filter === 'pending') return !d.completed;
|
||||
if (filter === 'completed') return d.completed;
|
||||
if (filter === 'overdue') return !d.completed && daysBetween(d.dueDate) < 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
filtered.sort((a, b) => {
|
||||
if (a.completed && !b.completed) return 1;
|
||||
if (!a.completed && b.completed) return -1;
|
||||
return new Date(a.dueDate) - new Date(b.dueDate);
|
||||
});
|
||||
|
||||
const isEs = lang === 'es';
|
||||
if (filtered.length === 0) {
|
||||
list.innerHTML = `<p class="text-muted" style="padding:12px;">${isEs ? 'No hay fechas límite para mostrar.' : 'No deadlines to display.'}</p>`;
|
||||
return;
|
||||
}
|
||||
|
||||
list.innerHTML = filtered.map(d => {
|
||||
const days = daysBetween(d.dueDate);
|
||||
const uClass = urgencyClass(days, d.completed);
|
||||
const uLabel = urgencyLabel(days, d.completed);
|
||||
const labelText = isEs ? uLabel.es : uLabel.en;
|
||||
const nameText = isEs ? (d.nameEs || d.name) : d.name;
|
||||
const dot = uClass === 'overdue' ? 'overdue' : uClass === 'completed' ? 'completed' : '';
|
||||
|
||||
return `<div class="fl-timeline-item ${dot}" data-id="${d.id}" data-completed="${d.completed}">
|
||||
<div class="fl-timeline-date">${new Date(d.dueDate).toLocaleDateString('en-US', {year:'numeric',month:'short',day:'numeric'})}</div>
|
||||
<div class="fl-timeline-title">${escapeHtml(nameText)}</div>
|
||||
<div class="fl-timeline-desc">
|
||||
<span class="fl-deadline-${uClass}">${escapeHtml(labelText)}</span>
|
||||
${d.statRef ? ` — <small class="text-muted">${escapeHtml(d.statRef)}</small>` : ''}
|
||||
</div>
|
||||
${!d.completed ? `<button class="btn btn-sm btn-outline-success fl-mark-complete mt-1" data-id="${d.id}">${isEs ? 'Marcar completo' : 'Mark complete'}</button>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function initTimeline() {
|
||||
const container = document.querySelector('.fl-timeline-widget');
|
||||
if (!container) return;
|
||||
|
||||
const dataEl = document.getElementById('fl-timeline-data');
|
||||
let deadlines = [];
|
||||
if (dataEl) {
|
||||
try { deadlines = JSON.parse(dataEl.textContent || '[]'); }
|
||||
catch (e) { console.warn('[FLTimeline] Could not parse timeline data', e); }
|
||||
}
|
||||
|
||||
let currentFilter = 'pending';
|
||||
let currentLang = document.body.classList.contains('fl-lang-es') ? 'es' : 'en';
|
||||
|
||||
renderTimeline(container, deadlines, currentFilter, currentLang);
|
||||
|
||||
container.querySelectorAll('.fl-timeline-filter').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
container.querySelectorAll('.fl-timeline-filter').forEach(b => b.classList.remove('active', 'btn-primary'));
|
||||
btn.classList.add('active', 'btn-primary');
|
||||
currentFilter = btn.dataset.filter;
|
||||
renderTimeline(container, deadlines, currentFilter, currentLang);
|
||||
});
|
||||
});
|
||||
|
||||
// Listen for language changes dispatched by fl_calculator.js
|
||||
document.body.addEventListener('fl-lang-change', (e) => {
|
||||
currentLang = e.detail && e.detail.lang || 'en';
|
||||
renderTimeline(container, deadlines, currentFilter, currentLang);
|
||||
});
|
||||
|
||||
// Mark complete via AJAX
|
||||
container.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.fl-mark-complete');
|
||||
if (!btn) return;
|
||||
const deadlineId = parseInt(btn.dataset.id);
|
||||
if (!deadlineId) return;
|
||||
btn.disabled = true;
|
||||
btn.textContent = '...';
|
||||
|
||||
fetch('/family-law/deadline/complete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
|
||||
body: JSON.stringify({ deadline_id: deadlineId }),
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const dl = deadlines.find(d => d.id === deadlineId);
|
||||
if (dl) dl.completed = true;
|
||||
renderTimeline(container, deadlines, currentFilter, currentLang);
|
||||
} else {
|
||||
btn.disabled = false;
|
||||
btn.textContent = currentLang === 'es' ? 'Marcar completo' : 'Mark complete';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
btn.disabled = false;
|
||||
btn.textContent = currentLang === 'es' ? 'Marcar completo' : 'Mark complete';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initTimeline);
|
||||
|
||||
export { initTimeline, renderTimeline, daysBetween, urgencyClass };
|
||||
|
||||
211
activeblue_familylaw/views/portal_calculator_templates.xml
Normal file
211
activeblue_familylaw/views/portal_calculator_templates.xml
Normal file
@@ -0,0 +1,211 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Portal FL 61.30 Interactive Child Support Calculator
|
||||
URL: /my/cases/calculator
|
||||
Uses fl_calculator.js for live FL 61.30 calculation.
|
||||
-->
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<template id="portal_calculator" name="FL 61.30 Child Support Calculator — Portal">
|
||||
<t t-call="portal.portal_layout">
|
||||
<div class="container fl-portal mt-4">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
|
||||
<li class="breadcrumb-item"><a href="/my/cases">My Cases</a></li>
|
||||
<li class="breadcrumb-item active">Support Calculator</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Language toggle -->
|
||||
<div class="fl-lang-toggle">
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn btn-primary" data-lang="en">EN</button>
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn" data-lang="es">ES</button>
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="fl-case-header mb-4">
|
||||
<h2>
|
||||
<span data-lang="en">FL 61.30 Child Support Calculator</span>
|
||||
<span data-lang="es" style="display:none;">Calculadora de Pensión Alimenticia FL 61.30</span>
|
||||
</h2>
|
||||
<div class="fl-case-meta">
|
||||
<span data-lang="en">Florida Child Support Guidelines — Section 61.30, Florida Statutes</span>
|
||||
<span data-lang="es" style="display:none;">Pautas de Manutención Infantil de Florida — Sección 61.30, Estatutos de Florida</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disclaimer -->
|
||||
<div class="alert alert-warning">
|
||||
<strong data-lang="en">⚠ Estimate Only — Not Legal Advice</strong>
|
||||
<strong data-lang="es" style="display:none;">⚠ Solo Estimación — No es Asesoramiento Legal</strong>
|
||||
<span data-lang="en"> This calculator provides an estimate based on the FL 61.30 schedule.
|
||||
The official court calculation may differ. Always verify with the court.</span>
|
||||
<span data-lang="es" style="display:none;"> Esta calculadora proporciona una estimación basada en el horario FL 61.30.
|
||||
El cálculo oficial del tribunal puede diferir. Siempre verifique con el tribunal.</span>
|
||||
</div>
|
||||
|
||||
<!-- Calculator form -->
|
||||
<div class="fl-calculator-section fl-calculator-widget">
|
||||
<div class="row">
|
||||
<!-- Column 1: Income -->
|
||||
<div class="col-md-6">
|
||||
<h5 class="text-primary mb-3" data-lang="en">Income Information</h5>
|
||||
<h5 class="text-primary mb-3" data-lang="es" style="display:none;">Información de Ingresos</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Petitioner Monthly Net Income</span>
|
||||
<span data-lang="es" style="display:none;">Ingreso Neto Mensual del Peticionario</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="petitionerNetIncome" class="form-control"
|
||||
placeholder="0.00" min="0" step="1"
|
||||
value="" t-if="prefill_petitioner"
|
||||
t-att-value="prefill_petitioner"/>
|
||||
<input type="number" id="petitionerNetIncome" class="form-control"
|
||||
placeholder="0.00" min="0" step="1"
|
||||
t-if="not prefill_petitioner"/>
|
||||
</div>
|
||||
<div class="form-text" data-lang="en">After taxes, FICA, and allowable deductions (FL 61.30(3))</div>
|
||||
<div class="form-text" data-lang="es" style="display:none;">Después de impuestos, FICA y deducciones permitidas (FL 61.30(3))</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Respondent Monthly Net Income</span>
|
||||
<span data-lang="es" style="display:none;">Ingreso Neto Mensual del Respondente</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="respondentNetIncome" class="form-control"
|
||||
placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Number of Children</span>
|
||||
<span data-lang="es" style="display:none;">Número de Hijos</span>
|
||||
</label>
|
||||
<select id="numChildren" class="form-select">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Column 2: Adjustments -->
|
||||
<div class="col-md-6">
|
||||
<h5 class="text-primary mb-3" data-lang="en">Adjustments & Timesharing</h5>
|
||||
<h5 class="text-primary mb-3" data-lang="es" style="display:none;">Ajustes y Tiempo Compartido</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Petitioner Health Insurance (child's share)</span>
|
||||
<span data-lang="es" style="display:none;">Seguro Médico del Peticionario (parte del hijo)</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="petitionerHealthInsurance" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Respondent Health Insurance (child's share)</span>
|
||||
<span data-lang="es" style="display:none;">Seguro Médico del Respondente (parte del hijo)</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="respondentHealthInsurance" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Petitioner Childcare (work-related)</span>
|
||||
<span data-lang="es" style="display:none;">Cuidado Infantil del Peticionario</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="petitionerChildcare" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Respondent Childcare (work-related)</span>
|
||||
<span data-lang="es" style="display:none;">Cuidado Infantil del Respondente</span>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" id="respondentChildcare" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Petitioner Overnights / Year</span>
|
||||
<span data-lang="es" style="display:none;">Noches del Peticionario / Año</span>
|
||||
</label>
|
||||
<input type="number" id="petitionerOvernights" class="form-control"
|
||||
placeholder="182" min="0" max="365" step="1" value="182"/>
|
||||
<div class="form-text" data-lang="en">Substantial timesharing (FL 61.30(11)(b)) applies when either parent >73 nights/year</div>
|
||||
<div class="form-text" data-lang="es" style="display:none;">El tiempo compartido sustancial aplica cuando cualquier padre tiene >73 noches/año</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">
|
||||
<span data-lang="en">Respondent Overnights / Year</span>
|
||||
<span data-lang="es" style="display:none;">Noches del Respondente / Año</span>
|
||||
</label>
|
||||
<input type="number" id="respondentOvernights" class="form-control"
|
||||
placeholder="183" min="0" max="365" step="1" value="183"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Result area (rendered by fl_calculator.js) -->
|
||||
<div class="fl-calculator-result-area mt-2"></div>
|
||||
<div class="fl-calculator-breakdown mt-3"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- How it works -->
|
||||
<div class="fl-section-card mt-4">
|
||||
<div class="fl-section-header" data-lang="en">How FL 61.30 Works</div>
|
||||
<div class="fl-section-header" data-lang="es" style="display:none;">Cómo Funciona FL 61.30</div>
|
||||
<div class="fl-section-body" style="font-size:0.9rem;">
|
||||
<div data-lang="en">
|
||||
<p><strong>Step 1:</strong> Add both parents' monthly net income (after taxes and allowable deductions).</p>
|
||||
<p><strong>Step 2:</strong> Look up the basic obligation from the FL 61.30 schedule based on combined income and number of children.</p>
|
||||
<p><strong>Step 3:</strong> Add health insurance and childcare costs to get the adjusted obligation.</p>
|
||||
<p><strong>Step 4:</strong> Each parent pays their proportional share (based on income %).</p>
|
||||
<p><strong>Step 5:</strong> If either parent has the children overnight more than 73 times per year, the substantial timesharing formula (FL 61.30(11)(b)) applies, which can significantly reduce the paying parent's obligation.</p>
|
||||
<p><strong>Modification:</strong> A modification is allowed when the new guideline amount differs by <strong>15% AND $50</strong> from the current order (FL 61.30(1)(b)).</p>
|
||||
</div>
|
||||
<div data-lang="es" style="display:none;">
|
||||
<p><strong>Paso 1:</strong> Sume los ingresos netos mensuales de ambos padres (después de impuestos y deducciones permitidas).</p>
|
||||
<p><strong>Paso 2:</strong> Busque la obligación básica en el horario FL 61.30 según los ingresos combinados y el número de hijos.</p>
|
||||
<p><strong>Paso 3:</strong> Agregue los costos de seguro médico y cuidado infantil para obtener la obligación ajustada.</p>
|
||||
<p><strong>Paso 4:</strong> Cada padre paga su parte proporcional (según % de ingresos).</p>
|
||||
<p><strong>Paso 5:</strong> Si cualquier padre tiene a los hijos de noche más de 73 veces al año, se aplica la fórmula de tiempo compartido sustancial (FL 61.30(11)(b)).</p>
|
||||
<p><strong>Modificación:</strong> Se permite una modificación cuando el nuevo monto de las pautas difiere en <strong>15% Y $50</strong> de la orden actual (FL 61.30(1)(b)).</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
316
activeblue_familylaw/views/portal_case_templates.xml
Normal file
316
activeblue_familylaw/views/portal_case_templates.xml
Normal file
@@ -0,0 +1,316 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Portal templates for fl.case — My Case page (authenticated litigant view)
|
||||
URL: /my/cases and /my/cases/<case_id>
|
||||
Inherits portal.portal_my_home and portal.portal_layout.
|
||||
-->
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- ══ Portal Home: add "My Cases" card ══════════════════════════════ -->
|
||||
<template id="portal_my_home_cases" name="FL Cases — Portal Home Entry"
|
||||
inherit_id="portal.portal_my_home" priority="30">
|
||||
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
|
||||
<t t-if="case_count">
|
||||
<div class="o_portal_doc_category">
|
||||
<t t-call="portal.portal_docs_entry">
|
||||
<t t-set="icon">/activeblue_familylaw/static/src/img/fl_icon.png</t>
|
||||
<t t-set="text">My Family Law Cases</t>
|
||||
<t t-set="url">/my/cases</t>
|
||||
<t t-set="count" t-value="case_count"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- ══ Case list page ════════════════════════════════════════════════ -->
|
||||
<template id="portal_my_cases" name="My Family Law Cases">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
<t t-call="portal.portal_searchbar">
|
||||
<t t-set="title">My Cases</t>
|
||||
</t>
|
||||
<div class="container fl-portal mt-4">
|
||||
<t t-if="not cases">
|
||||
<div class="alert alert-info">
|
||||
You have no family law cases on file. Contact ActiveBlue to open a new case.
|
||||
</div>
|
||||
</t>
|
||||
<t t-foreach="cases" t-as="case">
|
||||
<a t-attf-href="/my/cases/#{case.id}" class="text-decoration-none">
|
||||
<div class="fl-deadline-card mb-3">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-7">
|
||||
<div class="fl-deadline-type">
|
||||
<t t-esc="dict([('dissolution','Dissolution of Marriage'),('paternity','Paternity'),('child_support','Child Support'),('modification','Modification'),('injunction','Injunction')]).get(case.case_type, case.case_type)"/>
|
||||
</div>
|
||||
<div class="fl-deadline-name">
|
||||
<t t-esc="case.name"/>
|
||||
</div>
|
||||
<div class="fl-deadline-date text-muted" style="font-size:0.85rem;">
|
||||
Filed: <t t-esc="case.filing_date or 'Pending'"/>
|
||||
| Case No.: <t t-esc="case.court_case_number or 'Pending'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<t t-if="case.overdue_deadline_count">
|
||||
<span class="fl-deadline-overdue">
|
||||
⚠ <t t-esc="case.overdue_deadline_count"/> overdue deadline(s)
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<span class="badge bg-secondary">
|
||||
<t t-esc="case.stage_id.name if case.stage_id else 'Active'"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ══ Case detail page ══════════════════════════════════════════════ -->
|
||||
<template id="portal_case_detail" name="Case Detail — Portal">
|
||||
<t t-call="portal.portal_layout">
|
||||
<div class="container fl-portal mt-4">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
|
||||
<li class="breadcrumb-item"><a href="/my/cases">My Cases</a></li>
|
||||
<li class="breadcrumb-item active"><t t-esc="case.name"/></li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Language toggle -->
|
||||
<div class="fl-lang-toggle">
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn" data-lang="en">EN</button>
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn" data-lang="es">ES</button>
|
||||
</div>
|
||||
|
||||
<!-- Attorney referral banner (if AI flagged it) -->
|
||||
<t t-if="latest_analysis and latest_analysis.attorney_referral_flag">
|
||||
<div class="fl-attorney-referral-banner">
|
||||
<h5>⚠ Attorney Consultation Recommended</h5>
|
||||
<p><t t-esc="latest_analysis.attorney_referral_reason or 'Based on your case details, consulting with an attorney is strongly recommended.'"/></p>
|
||||
<p>
|
||||
<strong>Free Legal Resources:</strong>
|
||||
<a href="https://www.flvlp.org" target="_blank">FL Volunteer Lawyers Project</a> |
|
||||
<a href="https://www.fllsc.org" target="_blank">Legal Services of Greater Miami</a> |
|
||||
<a href="https://www.flcourts.gov/Resources-Services/Family-Courts" target="_blank">FL Courts Self-Help</a>
|
||||
</p>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- DV safety banner -->
|
||||
<t t-if="case.domestic_violence_flag">
|
||||
<div class="fl-dv-safety-banner">
|
||||
<div data-lang="en">
|
||||
<h5>🔴 Safety Resources</h5>
|
||||
<p>If you are in immediate danger, call <strong>911</strong>.<br/>
|
||||
National DV Hotline: <strong>1-800-799-7233</strong> (TTY: 1-800-787-3224)<br/>
|
||||
Miami-Dade Safe Space: <strong>(305) 758-2546</strong></p>
|
||||
<a href="https://www.google.com" class="fl-dv-safety-escape-link" target="_blank">Quick Exit (Google)</a>
|
||||
</div>
|
||||
<div data-lang="es" style="display:none;">
|
||||
<h5>🔴 Recursos de Seguridad</h5>
|
||||
<p>Si está en peligro inmediato, llame al <strong>911</strong>.<br/>
|
||||
Línea Nacional de Violencia Doméstica: <strong>1-800-799-7233</strong><br/>
|
||||
Miami-Dade Safe Space: <strong>(305) 758-2546</strong></p>
|
||||
<a href="https://www.google.com" class="fl-dv-safety-escape-link" target="_blank">Salida Rápida (Google)</a>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Case header -->
|
||||
<div class="fl-case-header">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<h2><t t-esc="case.name"/></h2>
|
||||
<div class="fl-case-meta">
|
||||
<t t-esc="dict([('dissolution','Dissolution of Marriage'),('paternity','Paternity'),('child_support','Child Support'),('modification','Modification'),('injunction','Injunction / DV')]).get(case.case_type, case.case_type)"/>
|
||||
<t t-if="case.court_case_number">
|
||||
| Case No. <t t-esc="case.court_case_number"/>
|
||||
</t>
|
||||
| Filed <t t-esc="case.filing_date or 'Pending'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="badge"><t t-esc="case.stage_id.name if case.stage_id else 'Active'"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats row -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="fl-stat-card <t t-if='case.overdue_deadline_count'>danger</t>">
|
||||
<div class="fl-stat-number"><t t-esc="case.overdue_deadline_count or 0"/></div>
|
||||
<div class="fl-stat-label">Overdue</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="fl-stat-card">
|
||||
<div class="fl-stat-number"><t t-esc="len(case.deadline_ids.filtered(lambda d: not d.completed))"/></div>
|
||||
<div class="fl-stat-label">Open Deadlines</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="fl-stat-card">
|
||||
<div class="fl-stat-number"><t t-esc="len(case.child_ids.filtered(lambda c: not c.emancipated))"/></div>
|
||||
<div class="fl-stat-label">Minor Children</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="fl-stat-card <t t-if='case.threshold_met'>success</t>">
|
||||
<div class="fl-stat-number" style="font-size:1.2rem;">
|
||||
<t t-if="case.threshold_met">✓ Met</t>
|
||||
<t t-else="">—</t>
|
||||
</div>
|
||||
<div class="fl-stat-label">Mod. Threshold</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI Summary -->
|
||||
<t t-if="latest_analysis and latest_analysis.plain_english_summary">
|
||||
<div class="fl-ai-summary">
|
||||
<div class="fl-ai-header">🤖 AI Case Summary</div>
|
||||
<div class="fl-ai-text" data-lang="en">
|
||||
<t t-esc="latest_analysis.plain_english_summary"/>
|
||||
</div>
|
||||
<div class="fl-ai-text" data-lang="es" style="display:none;">
|
||||
<t t-esc="latest_analysis.plain_english_summary_es or latest_analysis.plain_english_summary"/>
|
||||
</div>
|
||||
<div class="fl-ai-disclaimer">
|
||||
This is an AI-generated summary for informational purposes only. It is NOT legal advice.
|
||||
Always verify with a licensed Florida attorney.
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Deadlines / Timeline -->
|
||||
<div class="fl-section-card mb-4">
|
||||
<div class="fl-section-header">📅 Deadlines & Timeline</div>
|
||||
<div class="fl-section-body">
|
||||
<div class="fl-timeline-widget">
|
||||
<!-- Filter buttons -->
|
||||
<div class="btn-group btn-group-sm mb-3" role="group">
|
||||
<button type="button" class="btn btn-primary fl-timeline-filter active" data-filter="pending">Pending</button>
|
||||
<button type="button" class="btn btn-outline-secondary fl-timeline-filter" data-filter="overdue">Overdue</button>
|
||||
<button type="button" class="btn btn-outline-secondary fl-timeline-filter" data-filter="completed">Completed</button>
|
||||
<button type="button" class="btn btn-outline-secondary fl-timeline-filter" data-filter="all">All</button>
|
||||
</div>
|
||||
<!-- Deadline data for JS -->
|
||||
<script type="application/json" id="fl-timeline-data">
|
||||
[<t t-foreach="case.deadline_ids" t-as="dl">
|
||||
<t t-if="not dl_last">,</t>
|
||||
{"id": <t t-esc="dl.id"/>, "name": "<t t-esc="dl.name.replace('"', '').replace('\\', '')"/>", "dueDate": "<t t-esc="dl.due_date"/>", "completed": <t t-if="dl.completed">true</t><t t-else="">false</t>, "statRef": "<t t-esc="dl.statute_reference or ''"/>"}
|
||||
</t>]
|
||||
</script>
|
||||
<div class="fl-timeline">
|
||||
<div class="fl-timeline-list">
|
||||
<!-- Rendered by fl_timeline.js -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Support Info (if applicable) -->
|
||||
<t t-if="case.calculated_support or case.current_order_amount">
|
||||
<div class="fl-section-card mb-4">
|
||||
<div class="fl-section-header">💰 Child Support</div>
|
||||
<div class="fl-section-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="text-muted" style="font-size:0.82rem;">CURRENT ORDER</div>
|
||||
<div style="font-size:1.3rem;font-weight:700;color:#003366;">
|
||||
$<t t-esc="'%,.2f' % case.current_order_amount"/> / mo
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="case.calculated_support">
|
||||
<div class="col-md-4">
|
||||
<div class="text-muted" style="font-size:0.82rem;">GUIDELINE AMOUNT</div>
|
||||
<div style="font-size:1.3rem;font-weight:700;color:#003366;">
|
||||
$<t t-esc="'%,.2f' % case.calculated_support"/> / mo
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-muted" style="font-size:0.82rem;">THRESHOLD STATUS</div>
|
||||
<div style="font-size:1.1rem;font-weight:600;">
|
||||
<t t-if="case.threshold_met">
|
||||
<span class="text-success">✓ Modification threshold met</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span class="text-muted">Not met yet</span>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="/my/cases/calculator" class="btn btn-sm btn-outline-primary">
|
||||
Open Support Calculator →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Documents -->
|
||||
<t t-if="case.document_ids">
|
||||
<div class="fl-section-card mb-4">
|
||||
<div class="fl-section-header">📄 Documents</div>
|
||||
<div class="fl-section-body p-0">
|
||||
<t t-foreach="case.document_ids" t-as="doc">
|
||||
<a t-attf-href="/my/cases/#{case.id}/documents/#{doc.id}"
|
||||
class="fl-document-item">
|
||||
<div class="fl-doc-icon">📄</div>
|
||||
<div>
|
||||
<div class="fl-doc-name"><t t-esc="doc.name"/></div>
|
||||
<div class="fl-doc-type">
|
||||
<t t-esc="doc.document_type or 'Document'"/>
|
||||
</div>
|
||||
</div>
|
||||
<span class="fl-doc-badge <t t-if='doc.state == \"filed\"'>filed</t><t t-elif='doc.state == \"draft\"'>draft</t>">
|
||||
<t t-esc="(doc.state or 'draft').title()"/>
|
||||
</span>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Next Hearing -->
|
||||
<t t-if="case.next_hearing">
|
||||
<div class="fl-section-card mb-4">
|
||||
<div class="fl-section-header">🏛 Upcoming Hearing</div>
|
||||
<div class="fl-section-body">
|
||||
<strong><t t-esc="case.next_hearing.name"/></strong><br/>
|
||||
<t t-esc="case.next_hearing.hearing_date"/>
|
||||
<t t-if="case.next_hearing.location">
|
||||
| <t t-esc="case.next_hearing.location"/>
|
||||
</t>
|
||||
<div class="mt-2" style="font-size:0.85rem;">
|
||||
<t t-if="case.next_hearing.parenting_class_warning">
|
||||
<div class="text-warning">⚠ <t t-esc="case.next_hearing.parenting_class_warning"/></div>
|
||||
</t>
|
||||
<t t-if="case.next_hearing.discovery_cutoff_warning">
|
||||
<div class="text-warning">⚠ <t t-esc="case.next_hearing.discovery_cutoff_warning"/></div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
201
activeblue_familylaw/views/portal_caselaw_templates.xml
Normal file
201
activeblue_familylaw/views/portal_caselaw_templates.xml
Normal file
@@ -0,0 +1,201 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Portal Case Law Library — /my/cases/caselaw
|
||||
Searchable FL case law browser for pro se litigants.
|
||||
Bilingual EN/ES. Filterable by issue tag.
|
||||
-->
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<template id="portal_caselaw" name="FL Case Law Library — Portal">
|
||||
<t t-call="portal.portal_layout">
|
||||
<div class="container fl-portal mt-4">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/my/home">Home</a></li>
|
||||
<li class="breadcrumb-item active">Case Law Library</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Language toggle -->
|
||||
<div class="fl-lang-toggle">
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn btn-primary" data-lang="en">EN</button>
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn" data-lang="es">ES</button>
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<div class="fl-case-header mb-4">
|
||||
<h2>
|
||||
<span data-lang="en">Florida Family Law — Case Law Library</span>
|
||||
<span data-lang="es" style="display:none;">Derecho de Familia de Florida — Biblioteca de Jurisprudencia</span>
|
||||
</h2>
|
||||
<div class="fl-case-meta">
|
||||
<span data-lang="en">Key cases for child support modification, income imputation, timesharing, and more</span>
|
||||
<span data-lang="es" style="display:none;">Casos clave para modificación de manutención, imputación de ingresos, tiempo compartido y más</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disclaimer -->
|
||||
<div class="alert alert-info mb-4" style="font-size:0.88rem;">
|
||||
<span data-lang="en">
|
||||
<strong>Note:</strong> These case summaries are for educational purposes only.
|
||||
Always verify citations with the official Florida Reporter before citing in court.
|
||||
Holdings are summaries — read the full opinion for complete analysis.
|
||||
</span>
|
||||
<span data-lang="es" style="display:none;">
|
||||
<strong>Nota:</strong> Estos resúmenes de casos son solo para fines educativos.
|
||||
Siempre verifique las citas con el Florida Reporter oficial antes de citar en el tribunal.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Filter bar -->
|
||||
<div class="mb-4">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-md-5">
|
||||
<input type="text" id="caselaw-search" class="form-control form-control-sm"
|
||||
placeholder="Search cases, citations, holdings..."/>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select id="caselaw-tag-filter" class="form-select form-select-sm">
|
||||
<option value="">All Issues</option>
|
||||
<option value="modification_threshold">Modification Threshold (FL 61.30(1)(b))</option>
|
||||
<option value="income_imputation">Income Imputation</option>
|
||||
<option value="self_employment_income">Self-Employment Income</option>
|
||||
<option value="timesharing_deviation">Timesharing / FL 61.30(11)(b)</option>
|
||||
<option value="domestic_violence">Domestic Violence</option>
|
||||
<option value="default_judgment">Default Judgment</option>
|
||||
<option value="residency">Residency Requirement</option>
|
||||
<option value="parenting_class">Parenting Class</option>
|
||||
<option value="fee_waiver">Fee Waiver</option>
|
||||
<option value="post_order">Post-Order / Modification</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="caselaw-court-filter" class="form-select form-select-sm">
|
||||
<option value="">All Courts</option>
|
||||
<option value="fl_supreme">FL Supreme Court</option>
|
||||
<option value="3rd_dca">3rd DCA (Miami-Dade)</option>
|
||||
<option value="4th_dca">4th DCA</option>
|
||||
<option value="2nd_dca">2nd DCA</option>
|
||||
<option value="1st_dca">1st DCA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Case list -->
|
||||
<div id="caselaw-list">
|
||||
<t t-foreach="cases" t-as="cl">
|
||||
<div class="fl-section-card mb-3 caselaw-item"
|
||||
t-att-data-tags="','.join(cl.issue_tag_ids.mapped('name'))"
|
||||
t-att-data-court="cl.court"
|
||||
t-att-data-text="(cl.citation + ' ' + cl.holding).lower()">
|
||||
<div class="fl-section-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<em><t t-esc="cl.citation"/></em>
|
||||
</span>
|
||||
<span>
|
||||
<t t-foreach="cl.issue_tag_ids" t-as="tag">
|
||||
<span class="badge bg-secondary ms-1" style="font-size:0.72rem;">
|
||||
<t t-esc="tag.name"/>
|
||||
</span>
|
||||
</t>
|
||||
</span>
|
||||
</div>
|
||||
<div class="fl-section-body">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="mb-2">
|
||||
<strong data-lang="en">Holding:</strong>
|
||||
<strong data-lang="es" style="display:none;">Decisión:</strong>
|
||||
<span style="font-size:0.9rem;"> <t t-esc="cl.holding"/></span>
|
||||
</div>
|
||||
<t t-if="cl.plain_english">
|
||||
<div class="fl-ai-summary" style="padding:10px;">
|
||||
<div class="fl-ai-header" data-lang="en">Plain English</div>
|
||||
<div class="fl-ai-header" data-lang="es" style="display:none;">En Español Simple</div>
|
||||
<div class="fl-ai-text" data-lang="en" style="font-size:0.88rem;">
|
||||
<t t-esc="cl.plain_english"/>
|
||||
</div>
|
||||
<div class="fl-ai-text" data-lang="es" style="display:none;font-size:0.88rem;">
|
||||
<t t-esc="cl.plain_english_es or cl.plain_english"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<table class="table table-sm" style="font-size:0.82rem;">
|
||||
<tr>
|
||||
<td class="text-muted">Court:</td>
|
||||
<td>
|
||||
<t t-esc="dict([('fl_supreme','FL Supreme Court'),('3rd_dca','3rd DCA'),('4th_dca','4th DCA'),('2nd_dca','2nd DCA'),('1st_dca','1st DCA'),('5th_dca','5th DCA')]).get(cl.court, cl.court)"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">Year:</td>
|
||||
<td><t t-esc="cl.year"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">Favorable to:</td>
|
||||
<td>
|
||||
<t t-esc="dict([('petitioner','Petitioner'),('respondent','Respondent'),('neutral','Neutral'),('depends','Depends')]).get(cl.favorable_to, cl.favorable_to)"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<t t-if="cl.google_scholar_url">
|
||||
<a t-att-href="cl.google_scholar_url" target="_blank"
|
||||
class="btn btn-sm btn-outline-secondary w-100"
|
||||
rel="noopener noreferrer">
|
||||
View on Google Scholar →
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-if="not cases">
|
||||
<div class="alert alert-info">No cases in the library yet.</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<!-- No results message (shown by JS) -->
|
||||
<div id="caselaw-no-results" class="alert alert-warning" style="display:none;">
|
||||
<span data-lang="en">No cases match your search. Try different keywords or clear the filter.</span>
|
||||
<span data-lang="es" style="display:none;">Ningún caso coincide con su búsqueda. Pruebe diferentes palabras clave o elimine el filtro.</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Inline filter/search JS -->
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
function filterCases() {
|
||||
var search = document.getElementById('caselaw-search').value.toLowerCase();
|
||||
var tag = document.getElementById('caselaw-tag-filter').value;
|
||||
var court = document.getElementById('caselaw-court-filter').value;
|
||||
var items = document.querySelectorAll('.caselaw-item');
|
||||
var visible = 0;
|
||||
items.forEach(function(item) {
|
||||
var matchText = !search || (item.dataset.text || '').includes(search);
|
||||
var matchTag = !tag || (item.dataset.tags || '').split(',').includes(tag);
|
||||
var matchCourt = !court || item.dataset.court === court;
|
||||
var show = matchText && matchTag && matchCourt;
|
||||
item.style.display = show ? '' : 'none';
|
||||
if (show) visible++;
|
||||
});
|
||||
document.getElementById('caselaw-no-results').style.display = visible === 0 ? '' : 'none';
|
||||
}
|
||||
document.getElementById('caselaw-search').addEventListener('input', filterCases);
|
||||
document.getElementById('caselaw-tag-filter').addEventListener('change', filterCases);
|
||||
document.getElementById('caselaw-court-filter').addEventListener('change', filterCases);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
502
activeblue_familylaw/views/website_intake_templates.xml
Normal file
502
activeblue_familylaw/views/website_intake_templates.xml
Normal file
@@ -0,0 +1,502 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Public Website Intake Form — /family-law/intake
|
||||
Anonymous/public form for prospective pro se litigants.
|
||||
Multi-step wizard. Bilingual EN/ES.
|
||||
On submit → creates fl.intake.wizard record → creates fl.case.
|
||||
-->
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- ══ Website navbar item ══════════════════════════════════════════ -->
|
||||
<template id="website_navbar_intake" inherit_id="website.layout" name="FL Intake Nav">
|
||||
<xpath expr="//ul[hasclass('navbar-nav')]" position="inside">
|
||||
<li class="nav-item">
|
||||
<a href="/family-law/intake" class="nav-link">
|
||||
<span data-lang="en">Start My Case</span>
|
||||
<span data-lang="es" style="display:none;">Iniciar Mi Caso</span>
|
||||
</a>
|
||||
</li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- ══ Public landing page ══════════════════════════════════════════ -->
|
||||
<template id="website_intake_landing" name="FL Intake — Landing">
|
||||
<t t-call="website.layout">
|
||||
<div class="container mt-5 mb-5">
|
||||
|
||||
<!-- Language toggle -->
|
||||
<div class="text-end mb-3">
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn btn-primary" data-lang="en">EN</button>
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn" data-lang="es">ES</button>
|
||||
</div>
|
||||
|
||||
<!-- Hero -->
|
||||
<div class="text-center mb-5">
|
||||
<h1 style="color:#003366;" data-lang="en">Florida Family Law — Self-Help Portal</h1>
|
||||
<h1 style="color:#003366;" data-lang="es" style="display:none;">Portal de Autoayuda — Derecho de Familia de Florida</h1>
|
||||
<p class="lead text-muted" data-lang="en">
|
||||
Free tools for pro se litigants in Miami-Dade County (11th Circuit).<br/>
|
||||
Child support modification, paternity, dissolution, and more.
|
||||
</p>
|
||||
<p class="lead text-muted" data-lang="es" style="display:none;">
|
||||
Herramientas gratuitas para litigantes pro se en el Condado de Miami-Dade (Circuito 11).<br/>
|
||||
Modificación de manutención, paternidad, disolución y más.
|
||||
</p>
|
||||
<a href="/family-law/intake/start" class="btn btn-lg btn-primary mt-3 me-2"
|
||||
style="background:#003366;border-color:#003366;">
|
||||
<span data-lang="en">Start My Case →</span>
|
||||
<span data-lang="es" style="display:none;">Iniciar Mi Caso →</span>
|
||||
</a>
|
||||
<a href="/my/cases/calculator" class="btn btn-lg btn-outline-secondary mt-3">
|
||||
<span data-lang="en">Support Calculator</span>
|
||||
<span data-lang="es" style="display:none;">Calculadora de Pensión</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Service cards -->
|
||||
<div class="row g-4 mb-5">
|
||||
<div class="col-md-4">
|
||||
<div class="fl-section-card h-100">
|
||||
<div class="fl-section-header">Child Support Modification</div>
|
||||
<div class="fl-section-body">
|
||||
<p data-lang="en">Has your income or the other parent's income changed significantly?
|
||||
Florida allows modification when the new guideline amount differs by 15% AND $50 (FL 61.30(1)(b)).</p>
|
||||
<p data-lang="es" style="display:none;">¿Han cambiado significativamente sus ingresos o los del otro padre?
|
||||
Florida permite la modificación cuando el nuevo monto de las pautas difiere en un 15% Y $50.</p>
|
||||
<a href="/family-law/intake/start?case_type=modification" class="btn btn-sm btn-outline-primary">
|
||||
<span data-lang="en">Start Modification Case</span>
|
||||
<span data-lang="es" style="display:none;">Iniciar Caso de Modificación</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="fl-section-card h-100">
|
||||
<div class="fl-section-header">Paternity</div>
|
||||
<div class="fl-section-body">
|
||||
<p data-lang="en">Establish legal paternity, parenting rights, and child support for unmarried parents in Miami-Dade County.</p>
|
||||
<p data-lang="es" style="display:none;">Establecer paternidad legal, derechos de crianza y manutención infantil para padres no casados en el Condado de Miami-Dade.</p>
|
||||
<a href="/family-law/intake/start?case_type=paternity" class="btn btn-sm btn-outline-primary">
|
||||
<span data-lang="en">Start Paternity Case</span>
|
||||
<span data-lang="es" style="display:none;">Iniciar Caso de Paternidad</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="fl-section-card h-100">
|
||||
<div class="fl-section-header">Dissolution of Marriage</div>
|
||||
<div class="fl-section-body">
|
||||
<p data-lang="en">File for divorce in Florida. Residency requirement: 6 months in Florida before filing (FL 61.021).</p>
|
||||
<p data-lang="es" style="display:none;">Presentar una solicitud de divorcio en Florida. Requisito de residencia: 6 meses en Florida antes de presentar (FL 61.021).</p>
|
||||
<a href="/family-law/intake/start?case_type=dissolution" class="btn btn-sm btn-outline-primary">
|
||||
<span data-lang="en">Start Dissolution Case</span>
|
||||
<span data-lang="es" style="display:none;">Iniciar Caso de Disolución</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resources -->
|
||||
<div class="fl-section-card mb-4">
|
||||
<div class="fl-section-header" data-lang="en">Free Legal Resources — Miami-Dade</div>
|
||||
<div class="fl-section-header" data-lang="es" style="display:none;">Recursos Legales Gratuitos — Miami-Dade</div>
|
||||
<div class="fl-section-body">
|
||||
<div class="row g-3" style="font-size:0.9rem;">
|
||||
<div class="col-md-6">
|
||||
<strong>FL Volunteer Lawyers Project:</strong>
|
||||
<a href="https://www.flvlp.org" target="_blank">flvlp.org</a><br/>
|
||||
<strong>Legal Services of Greater Miami:</strong>
|
||||
<a href="https://www.lsgmi.org" target="_blank">lsgmi.org</a><br/>
|
||||
<strong>Miami-Dade Clerk Self-Help Center:</strong>
|
||||
<a href="https://www.miamidadeclerk.gov/family" target="_blank">miamidadeclerk.gov</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<strong>FL Courts Self-Help:</strong>
|
||||
<a href="https://www.flcourts.gov/Resources-Services/Family-Courts" target="_blank">flcourts.gov</a><br/>
|
||||
<strong>FL DOR Child Support eServices:</strong>
|
||||
<a href="https://floridarevenue.com/childsupport" target="_blank">floridarevenue.com/childsupport</a><br/>
|
||||
<strong>DV Hotline (24/7):</strong> 1-800-799-7233
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disclaimer -->
|
||||
<div class="text-center text-muted" style="font-size:0.8rem;">
|
||||
<span data-lang="en">ActiveBlue Family Law is a case management tool — NOT a law firm.
|
||||
Nothing on this website constitutes legal advice. Consult a licensed Florida attorney for legal guidance.</span>
|
||||
<span data-lang="es" style="display:none;">ActiveBlue Family Law es una herramienta de gestión de casos, NO una firma de abogados.
|
||||
Nada en este sitio web constituye asesoramiento legal. Consulte a un abogado con licencia de Florida.</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ══ Multi-step intake form ═══════════════════════════════════════ -->
|
||||
<template id="website_intake_form" name="FL Intake — Case Setup Form">
|
||||
<t t-call="website.layout">
|
||||
<div class="container mt-4 mb-5">
|
||||
|
||||
<!-- Language toggle -->
|
||||
<div class="text-end mb-3">
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn btn-primary" data-lang="en">EN</button>
|
||||
<button class="btn btn-outline-secondary btn-sm fl-lang-btn" data-lang="es">ES</button>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
|
||||
<!-- Header -->
|
||||
<h2 style="color:#003366;" class="mb-1" data-lang="en">Start Your Case</h2>
|
||||
<h2 style="color:#003366;" class="mb-1" data-lang="es" style="display:none;">Iniciar Su Caso</h2>
|
||||
<p class="text-muted mb-4" data-lang="en">Fill in the information below. We'll help you generate the right court forms and track your deadlines.</p>
|
||||
<p class="text-muted mb-4" data-lang="es" style="display:none;">Complete la información a continuación. Le ayudaremos a generar los formularios judiciales correctos y rastrear sus fechas límite.</p>
|
||||
|
||||
<!-- Progress bar -->
|
||||
<div class="fl-intake-progress mb-4">
|
||||
<div class="fl-intake-progress-step active"></div>
|
||||
<div class="fl-intake-progress-step"></div>
|
||||
<div class="fl-intake-progress-step"></div>
|
||||
<div class="fl-intake-progress-step"></div>
|
||||
</div>
|
||||
<div class="text-muted mb-3 small fl-step-counter">Step 1 of 4</div>
|
||||
|
||||
<!-- DV safety check (always first) -->
|
||||
<div class="alert alert-warning mb-4" style="font-size:0.88rem;">
|
||||
<span data-lang="en"><strong>Safety Check:</strong> If you are in danger, call 911.
|
||||
DV Hotline: 1-800-799-7233. Miami-Dade Safe Space: (305) 758-2546.
|
||||
<a href="https://www.google.com" target="_blank" class="btn btn-sm btn-danger ms-2">Quick Exit</a></span>
|
||||
<span data-lang="es" style="display:none;"><strong>Verificación de Seguridad:</strong> Si está en peligro, llame al 911.
|
||||
Línea de violencia doméstica: 1-800-799-7233.
|
||||
<a href="https://www.google.com" target="_blank" class="btn btn-sm btn-danger ms-2">Salida Rápida</a></span>
|
||||
</div>
|
||||
|
||||
<form method="post" action="/family-law/intake/submit" class="fl-intake-form"
|
||||
t-att-data-case-type="case_type or ''">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="case_type" t-att-value="case_type or 'child_support'" id="intake_case_type"/>
|
||||
|
||||
<!-- STEP 1: Case Type & Basic Info -->
|
||||
<div class="fl-intake-step active" id="step-1">
|
||||
<h5 class="mb-3" data-lang="en">Step 1 — Case Type & Your Information</h5>
|
||||
<h5 class="mb-3" data-lang="es" style="display:none;">Paso 1 — Tipo de Caso e Información Personal</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Type of Case</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Tipo de Caso</label>
|
||||
<select name="case_type_select" id="case_type_select" class="form-select"
|
||||
onchange="document.getElementById('intake_case_type').value=this.value">
|
||||
<option value="modification" t-att-selected="case_type == 'modification' and 'selected' or None">Child Support Modification (FL 61.14)</option>
|
||||
<option value="paternity" t-att-selected="case_type == 'paternity' and 'selected' or None">Establish Paternity (FL 742.10)</option>
|
||||
<option value="child_support" t-att-selected="(not case_type or case_type == 'child_support') and 'selected' or None">Child Support (New Order)</option>
|
||||
<option value="dissolution" t-att-selected="case_type == 'dissolution' and 'selected' or None">Dissolution of Marriage (FL 61.052)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Your Full Legal Name</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Su Nombre Legal Completo</label>
|
||||
<input type="text" name="petitioner_name" class="form-control"
|
||||
placeholder="First Middle Last" required="required"/>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold" data-lang="en">Your Email</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Su Correo Electrónico</label>
|
||||
<input type="email" name="petitioner_email" class="form-control"
|
||||
placeholder="you@email.com" required="required"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold" data-lang="en">Your Phone</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Su Teléfono</label>
|
||||
<input type="tel" name="petitioner_phone" class="form-control" placeholder="(305) 555-0000"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Your Address (Miami-Dade County)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Su Dirección (Condado de Miami-Dade)</label>
|
||||
<input type="text" name="petitioner_address" class="form-control" placeholder="Street Address"/>
|
||||
<input type="text" name="petitioner_city" class="form-control mt-2" placeholder="City"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Florida Resident Since</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Residente de Florida Desde</label>
|
||||
<input type="date" name="fl_resident_since" class="form-control"/>
|
||||
<div class="form-text" data-lang="en">Must be at least 6 months before filing date for dissolution (FL 61.021).</div>
|
||||
<div class="form-text" data-lang="es" style="display:none;">Debe ser al menos 6 meses antes de la fecha de presentación para la disolución (FL 61.021).</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-primary fl-next-btn">
|
||||
<span data-lang="en">Next →</span>
|
||||
<span data-lang="es" style="display:none;">Siguiente →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 2: Other Party & Children -->
|
||||
<div class="fl-intake-step" id="step-2">
|
||||
<h5 class="mb-3" data-lang="en">Step 2 — Other Party & Children</h5>
|
||||
<h5 class="mb-3" data-lang="es" style="display:none;">Paso 2 — Otra Parte e Hijos</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Other Party's Full Name (Respondent)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Nombre Completo de la Otra Parte (Respondente)</label>
|
||||
<input type="text" name="respondent_name" class="form-control"
|
||||
placeholder="First Middle Last" required="required"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Does the other party have an attorney?</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">¿La otra parte tiene un abogado?</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="respondent_has_counsel" value="yes" id="counsel_yes"/>
|
||||
<label class="form-check-label" for="counsel_yes" data-lang="en">Yes</label>
|
||||
<label class="form-check-label" for="counsel_yes" data-lang="es" style="display:none;">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="respondent_has_counsel" value="no" id="counsel_no" checked="checked"/>
|
||||
<label class="form-check-label" for="counsel_no" data-lang="en">No</label>
|
||||
<label class="form-check-label" for="counsel_no" data-lang="es" style="display:none;">No</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text text-danger" data-lang="en">If the other party has an attorney, we strongly recommend you consult one too.</div>
|
||||
<div class="form-text text-danger" data-lang="es" style="display:none;">Si la otra parte tiene un abogado, le recomendamos encarecidamente que también consulte a uno.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Number of Minor Children</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Número de Hijos Menores</label>
|
||||
<select name="num_children" class="form-select">
|
||||
<option value="0">0 (no minor children)</option>
|
||||
<option value="1" selected="selected">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5+</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Existing Court Case Number (if modifying)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Número de Caso del Tribunal Existente (si modifica)</label>
|
||||
<input type="text" name="court_case_number" class="form-control"
|
||||
placeholder="e.g. 2023-DR-12345"/>
|
||||
<div class="form-text" data-lang="en">Leave blank if this is a new case.</div>
|
||||
<div class="form-text" data-lang="es" style="display:none;">Déjelo en blanco si es un caso nuevo.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Is there a history of domestic violence?</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">¿Existe un historial de violencia doméstica?</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="domestic_violence" value="yes" id="dv_yes"/>
|
||||
<label class="form-check-label" for="dv_yes" data-lang="en">Yes</label>
|
||||
<label class="form-check-label" for="dv_yes" data-lang="es" style="display:none;">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="domestic_violence" value="no" id="dv_no" checked="checked"/>
|
||||
<label class="form-check-label" for="dv_no" data-lang="en">No</label>
|
||||
<label class="form-check-label" for="dv_no" data-lang="es" style="display:none;">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-outline-secondary fl-prev-btn" data-lang="en">← Back</button>
|
||||
<button type="button" class="btn btn-outline-secondary fl-prev-btn" data-lang="es" style="display:none;">← Atrás</button>
|
||||
<button type="button" class="btn btn-primary fl-next-btn">
|
||||
<span data-lang="en">Next →</span>
|
||||
<span data-lang="es" style="display:none;">Siguiente →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 3: Income & Financial -->
|
||||
<div class="fl-intake-step" id="step-3">
|
||||
<h5 class="mb-3" data-lang="en">Step 3 — Income Information</h5>
|
||||
<h5 class="mb-3" data-lang="es" style="display:none;">Paso 3 — Información de Ingresos</h5>
|
||||
|
||||
<div class="alert alert-info" style="font-size:0.85rem;" data-lang="en">
|
||||
Use <strong>monthly net income</strong> (after taxes, FICA, and required deductions per FL 61.30(3)).
|
||||
</div>
|
||||
<div class="alert alert-info" style="font-size:0.85rem;" data-lang="es" style="display:none;">
|
||||
Use el <strong>ingreso neto mensual</strong> (después de impuestos, FICA y deducciones requeridas por FL 61.30(3)).
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold" data-lang="en">Your Monthly Net Income</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Su Ingreso Neto Mensual</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" name="petitioner_income" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold" data-lang="en">Other Party's Monthly Net Income (estimated)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Ingreso Neto Mensual de la Otra Parte (estimado)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" name="respondent_income" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Do you believe the other party is hiding income or voluntarily underemployed?</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">¿Cree que la otra parte está ocultando ingresos o está voluntariamente subempleada?</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="income_imputation_concern" value="yes" id="imp_yes"/>
|
||||
<label class="form-check-label" for="imp_yes">Yes</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="income_imputation_concern" value="no" id="imp_no" checked="checked"/>
|
||||
<label class="form-check-label" for="imp_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Current Monthly Child Support Order (if modifying)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Orden Actual de Manutención Mensual (si modifica)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" name="current_order_amount" class="form-control" placeholder="0.00" min="0" step="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-outline-secondary fl-prev-btn">← Back</button>
|
||||
<button type="button" class="btn btn-primary fl-next-btn">
|
||||
<span data-lang="en">Next →</span>
|
||||
<span data-lang="es" style="display:none;">Siguiente →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- STEP 4: Fee Waiver & Submit -->
|
||||
<div class="fl-intake-step" id="step-4">
|
||||
<h5 class="mb-3" data-lang="en">Step 4 — Fee Waiver & Submit</h5>
|
||||
<h5 class="mb-3" data-lang="es" style="display:none;">Paso 4 — Exención de Tarifas y Enviar</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold" data-lang="en">Household Size (including yourself)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Tamaño del Hogar (incluyéndose usted)</label>
|
||||
<select name="household_size" class="form-select">
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3" selected="selected">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6+</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="fee_waiver_request" id="fee_waiver_check" value="yes"/>
|
||||
<label class="form-check-label" for="fee_waiver_check" data-lang="en">
|
||||
I would like to apply for a filing fee waiver (FL 57.082 — Civil Indigent Status).
|
||||
Eligibility: household income at or below 200% of federal poverty level.
|
||||
</label>
|
||||
<label class="form-check-label" for="fee_waiver_check" data-lang="es" style="display:none;">
|
||||
Deseo solicitar una exención de tarifas de presentación (FL 57.082 — Estado Civil Indigente).
|
||||
Elegibilidad: ingresos del hogar iguales o inferiores al 200% del nivel federal de pobreza.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold" data-lang="en">Additional Notes (optional)</label>
|
||||
<label class="form-label fw-semibold" data-lang="es" style="display:none;">Notas Adicionales (opcional)</label>
|
||||
<textarea name="notes" class="form-control" rows="3"
|
||||
placeholder="Any other information you'd like us to know about your case..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="acknowledge_disclaimer" id="acknowledge" required="required"/>
|
||||
<label class="form-check-label" for="acknowledge" style="font-size:0.85rem;" data-lang="en">
|
||||
I understand that ActiveBlue Family Law is a case management tool, NOT a law firm.
|
||||
This service does not provide legal advice. I will verify all documents with
|
||||
the court before filing. <strong>Required to submit.</strong>
|
||||
</label>
|
||||
<label class="form-check-label" for="acknowledge" style="font-size:0.85rem;display:none;" data-lang="es">
|
||||
Entiendo que ActiveBlue Family Law es una herramienta de gestión de casos, NO una firma de abogados.
|
||||
Este servicio no proporciona asesoramiento legal. Verificaré todos los documentos con el tribunal antes de presentarlos.
|
||||
<strong>Requerido para enviar.</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-outline-secondary fl-prev-btn">← Back</button>
|
||||
<button type="submit" class="btn btn-success btn-lg">
|
||||
<span data-lang="en">Submit & Create My Case →</span>
|
||||
<span data-lang="es" style="display:none;">Enviar y Crear Mi Caso →</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ══ Intake confirmation page ════════════════════════════════════ -->
|
||||
<template id="website_intake_confirm" name="FL Intake — Confirmation">
|
||||
<t t-call="website.layout">
|
||||
<div class="container mt-5 mb-5 text-center">
|
||||
<div style="font-size:3rem;">✅</div>
|
||||
<h2 style="color:#003366;" data-lang="en">Your Case Has Been Created!</h2>
|
||||
<h2 style="color:#003366;" data-lang="es" style="display:none;">¡Su Caso Ha Sido Creado!</h2>
|
||||
<p class="lead" data-lang="en">
|
||||
Case <strong><t t-esc="case_name"/></strong> is now open.
|
||||
An email confirmation has been sent to <strong><t t-esc="petitioner_email"/></strong>.
|
||||
</p>
|
||||
<p class="lead" data-lang="es" style="display:none;">
|
||||
El caso <strong><t t-esc="case_name"/></strong> ya está abierto.
|
||||
Se ha enviado una confirmación por correo electrónico a <strong><t t-esc="petitioner_email"/></strong>.
|
||||
</p>
|
||||
<div class="mt-4">
|
||||
<a href="/my/cases" class="btn btn-primary btn-lg me-2" data-lang="en">View My Cases →</a>
|
||||
<a href="/my/cases" class="btn btn-primary btn-lg me-2" data-lang="es" style="display:none;">Ver Mis Casos →</a>
|
||||
<a href="/my/cases/calculator" class="btn btn-outline-secondary btn-lg" data-lang="en">Support Calculator</a>
|
||||
<a href="/my/cases/calculator" class="btn btn-outline-secondary btn-lg" data-lang="es" style="display:none;">Calculadora</a>
|
||||
</div>
|
||||
<t t-if="fee_waiver_eligible">
|
||||
<div class="alert alert-success mt-4 text-start" style="max-width:600px;margin:auto;">
|
||||
<strong data-lang="en">💚 You may qualify for a filing fee waiver (FL 57.082).</strong>
|
||||
<strong data-lang="es" style="display:none;">💚 Puede calificar para una exención de tarifas de presentación (FL 57.082).</strong>
|
||||
<span data-lang="en"> A Fee Waiver Application has been prepared in your case documents.</span>
|
||||
<span data-lang="es" style="display:none;"> Una Solicitud de Exención de Tarifas ha sido preparada en los documentos de su caso.</span>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="attorney_referral">
|
||||
<div class="fl-attorney-referral-banner mt-4 text-start" style="max-width:600px;margin:auto;">
|
||||
<h5 data-lang="en">⚠ Attorney Consultation Recommended</h5>
|
||||
<h5 data-lang="es" style="display:none;">⚠ Se Recomienda Consulta con Abogado</h5>
|
||||
<p data-lang="en">Based on your case details (opposing counsel or DV concern), we strongly recommend
|
||||
consulting with a licensed Florida attorney before proceeding.
|
||||
<a href="https://www.flvlp.org" target="_blank">FL Volunteer Lawyers Project</a></p>
|
||||
<p data-lang="es" style="display:none;">Según los detalles de su caso, recomendamos encarecidamente consultar con un abogado de Florida con licencia antes de continuar.</p>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user