Files
famlaw/activeblue_familylaw/views/portal_case_templates.xml
Carlos Garcia 6dc2144db7 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>
2026-05-06 23:42:37 -05:00

317 lines
19 KiB
XML

<?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'"/>
&nbsp;|&nbsp; 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">
&nbsp;| Case No. <t t-esc="case.court_case_number"/>
</t>
&nbsp;| 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 &amp; 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('&quot;', '').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">
&nbsp;| <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>