Files
famlaw/activeblue_familylaw/views/portal_caselaw_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

202 lines
13 KiB
XML

<?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 &amp;&amp; matchTag &amp;&amp; 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>