Session: 276e14f3-4bec-4093-a04c-f3db3accdd72

CWD: /var/lib/metahuman-ocr-worker/work/job-88/worktree Branch: HEAD Mode: range From: origin/new_staging2 To: origin/hotfix/ssma-descaracter-card-sync-fix Model: deepseek-v4-flash Duration: 3m19s Files: 2 Status: complete

Coverage

2
Selected
2
Completed
0
Reused
0
Failed
0
Waived

Token Usage

551.06K
Prompt Tokens
25.98K
Completion Tokens
577.04K
Total Tokens
24
LLM Requests
512.64K
Cache Read
0
Cache Write
File breakdown 1 files
FilePromptCompletionCache ReadCache WriteTotal
.opencodereview/rule.json,templates/ssma/occurrence/partials… 551.06K 25.98K 512.64K0 577.04K

Review Comments (1 findings)

Severity:
Category:
templates/ssma/occurrence/partials/_modal_event.html.twig 1 comments
maintainability low L2709
A correção é coerente com o fluxo descrito, mas reforça o padrão de orquestração de UI dentro do `<script>` gigante embutido no template (arquivo com ~7,7 mil linhas misturando markup e lógica de tela). Isso dificulta teste unitário, reaproveitamento e manutenção, e o padrão do projeto manda lógica de montagem de card/AJAX para `public/js/`. Não bloqueia este hotfix de 2 linhas, mas vale registrar a dívida de migrar `evSyncDescaracterStageUi` e os handlers de card para um JS dedicado na próxima alteração neste fluxo.
Existing Code
        evSyncDescaracterStageUi();
Files Reviewed 2 files
  • .opencodereview/rule.json
  • templates/ssma/occurrence/partials/_modal_event.html.twig
Conversations 1 files
.opencodereview/rule.json,templates/ssma/occurrence/partials/_modal_event.html.twig 24 requests

main_task

Request #1 deepseek-v4-flash P:6.11K C:304 CR:2.69K CW:0 3007ms
Reasoning
Let me review the code changes in the files provided. Files to review: 1. `.opencodereview/rule.json` — JSON file with spelling check for JSON keys. 2. `templates/ssma/occurrence/partials/_modal_event.html.twig` — added 2 calls to `evSyncDescaracterStageUi()`. Let me start by reading the relevant files for context. First, let me look at the JSON rule file. The rules say to check spelling errors in json-keys, ignoring content of json-values. Let me read the file. Then for the twig template, I need to understand the context. Let me search for `evSyncDescaracterStageUi`, `evCreateInjuredPersonCard`, `evRenderInjuredPersonBoxes` in the template file to understand. Let me gather context.
Tool Calls (2)
file_read
Show Details
{"file_path": ".opencodereview/rule.json"}
File: .opencodereview/rule.json (Total lines: 137)
IS_TRUNCATED: false
LINE_RANGE: 1-137
1|{
2|  "include": [
3|    "tests/**/*.php",
4|    "**/*.twig"
5|  ],
6|  "rules": [
7|    {
8|      "path": "src/Service/Interview/V2/**/*.php",
9|      "merge_system_rule": true,
10|      "rule": ".opencodereview/rules/interview-v2/service.md"
11|    },
12|    {
13|      "path": "src/Controller/Interview/V2/InterviewConversationV2Controller.php",
14|      "rule": ".opencodereview/rules/interview-v2/controller.md"
15|    },
16|    {
17|      "path": "src/Prompt/Interview/V2/Conversation/{ConversationSystemPrompt,MediaInteractionPrompt}.php",
18|      "rule": ".opencodereview/rules/interview-v2/prompt.md"
19|    },
20|    {
21|      "path": "tests/Unit/Product/PesquisaIaV2/**/*.php",
22|      "merge_system_rule": true,
23|      "rule": ".opencodereview/rules/interview-v2/tests.md"
24|    },
25|    {
26|      "path": "src/Service/ProjectCollaboratorAccessService.php",
27|      "merge_system_rule": true,
28|      "rule": ".opencodereview/rules/projects/task-deadline.md"
29|    },
30|    {
31|      "path": "src/Controller/ProjectsNewController.php",
32|      "merge_system_rule": true,
33|      "rule": ".opencodereview/rules/projects/task-deadline.md"
34|    },
35|    {
36|      "path": "public/js/projects/**/*.js",
37|      "merge_system_rule": true,
38|      "rule": ".opencodereview/rules/projects/task-deadline.md"
39|    },
40|    {
41|      "path": "templates/projects2.0/**/*.twig",
42|      "merge_system_rule": true,
43|      "rule": ".opencodereview/rules/projects/task-deadline.md"
44|    },
45|    {
46|      "path": "tests/Unit/Product/Projects/**/*.php",
47|      "merge_system_rule": true,
48|      "rule": ".opencodereview/rules/projects/task-deadline.md"
49|    },
50|    {
51|      "path": "src/Service/Ssma/ActionPlan/SsmaActionPlanPanel{Service,Presenter}.php",
52|      "merge_system_rule": true,
53|      "rule": ".opencodereview/rules/ssma/action-plan-panel.md"
54|    },
55|    {
56|      "path": "public/js/ssma/action_plan_panel.js",
57|      "merge_system_rule": true,
58|      "rule": ".opencodereview/rules/ssma/action-plan-panel.md"
59|    },
60|    {
61|      "path": "templates/ssma/action_plan/tabs/_tab_painel*.html.twig",
62|      "merge_system_rule": true,
63|      "rule": ".opencodereview/rules/ssma/action-plan-panel.md"
64|    },
65|    {
66|      "path": "templates/ssma/occurrence/partials/_modal_event.html.twig",
67|      "merge_system_rule": true,
68|      "rule": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md"
69|    },
70|    {
71|      "path": "templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig",
72|      "merge_system_rule": true,
73|      "rule": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md"
74|    },
75|    {
76|      "path": "src/Controller/SsmaController.php",
77|      "merge_system_rule": true,
78|      "rule": ".opencodereview/rules/ssma/occurrence-approve.md"
79|    },
80|    {
81|      "path": "templates/ssma/occurrence/occurrence_view.html.twig",
82|      "merge_system_rule": true,
83|      "rule": ".opencodereview/rules/ssma/occurrence-approve.md"
84|    },
85|    {
86|      "path": "src/Controller/**/*.php",
87|      "merge_system_rule": true,
88|      "rule": ".opencodereview/rules/backend/controller.md"
89|    },
90|    {
91|      "path": "src/Service/**/*.php",
92|      "merge_system_rule": true,
93|      "rule": ".opencodereview/rules/backend/service.md"
94|    },
95|    {
96|      "path": "src/Repository/**/*.php",
97|      "merge_system_rule": true,
98|      "rule": ".opencodereview/rules/backend/repository.md"
99|    },
100|    {
101|      "path": "src/{EventListener,EventSubscriber,Security/Voter}/**/*.php",
102|      "merge_system_rule": true,
103|      "rule": ".opencodereview/rules/backend/listener-voter.md"
104|    },
105|    {
106|      "path": "src/Command/**/*.php",
107|      "merge_system_rule": true,
108|      "rule": ".opencodereview/rules/backend/command.md"
109|    },
110|    {
111|      "path": "migrations/**/*.php",
112|      "merge_system_rule": true,
113|      "rule": ".opencodereview/rules/backend/migrations.md"
114|    },
115|    {
116|      "path": "src/**/*.php",
117|      "merge_system_rule": true,
118|      "rule": ".opencodereview/rules/backend/php-general.md"
119|    },
120|    {
121|      "path": "templates/**/*.twig",
122|      "merge_system_rule": true,
123|      "rule": ".opencodereview/rules/frontend/twig.md"
124|    },
125|    {
126|      "path": "public/js/**/*.js",
127|      "merge_system_rule": true,
128|      "rule": ".opencodereview/rules/frontend/js.md"
129|    },
130|    {
131|      "path": "tests/**/*.php",
132|      "merge_system_rule": true,
133|      "rule": ".opencodereview/rules/backend/tests.md"
134|    }
135|  ]
136|}
137|
code_search
Show Details
{"search_text": "evSyncDescaracterStageUi", "file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"]}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 8
2709|        evSyncDescaracterStageUi();
2826|        evSyncDescaracterStageUi();
2917|    function evSyncDescaracterStageUi() {
2947|        evSyncDescaracterStageUi();
3323|        evSyncDescaracterStageUi();
3398|        evSyncDescaracterStageUi();
5288|        evSyncDescaracterStageUi();
5736|                evSyncDescaracterStageUi();

Request #2 deepseek-v4-flash P:8.23K C:241 CR:6.4K CW:0 1849ms
Reasoning
Let me examine the context of the function `evSyncDescaracterStageUi` and the two added call sites. Let me read the surrounding lines. Let me read around lines 2680-2740 and 2790-2850 and 2900-2970 to understand the logic.
Tool Calls (2)
file_read
Show Details
{"file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2640, "end_line": 2870}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2640-2870
2640|        card.querySelectorAll('.ev-inj-descaracter-comment').forEach(function (ta) {
2641|            ta.addEventListener('input', function () {
2642|                ta.classList.remove('is-invalid');
2643|            });
2644|        });
2645|        evFilterCardConsequences(card);
2646|        evSyncCardCatFromClassification(card);
2647|        evSyncDescaracterUi();
2648|        if (window.jQuery && window.jQuery.fn.tooltip) {
2649|            window.jQuery(card).find('.ev-inj-descaracter-tip').tooltip({ container: 'body' });
2650|        }
2651|    }
2652|
2653|    function evCreateInjuredPersonCard(personId, saved, expand) {
2654|        var wrap = document.getElementById('ev_injured_person_boxes');
2655|        var tpl = document.getElementById('ev-injured-person-box-tpl');
2656|        if (!wrap || !tpl || !personId) return null;
2657|        var safe = String(personId).replace(/"/g, '');
2658|        var existing = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2659|        if (existing) return existing;
2660|
2661|        var source = tpl.querySelector('.ev-injured-person-box') || tpl.firstElementChild;
2662|        if (!source) return null;
2663|        var card = source.cloneNode(true);
2664|        if (!card || !card.classList) return null;
2665|        card.classList.remove('d-none');
2666|
2667|        var involved = evGetPeopleInvolved();
2668|        var sel = document.getElementById('ev_person_id');
2669|        var opt = sel ? sel.querySelector('option[value="' + safe + '"]') : null;
2670|        var name = involved.names[personId]
2671|            || (opt ? String(opt.text || '').trim() : '')
2672|            || ('#' + personId);
2673|
2674|        card.setAttribute('data-person-id', String(personId));
2675|        var nameEl = card.querySelector('.ev-inj-person-name');
2676|        if (nameEl) nameEl.textContent = name;
2677|        var sumName = card.querySelector('.js-ev-inj-summary-name');
2678|        if (sumName) sumName.textContent = name;
2679|
2680|        saved = saved || {};
2681|        if (!saved.attendance_date) saved.attendance_date = evTodayDateInputValue();
2682|        if (saved.had_injury === undefined) saved.had_injury = true;
2683|        evApplyCardInjuryData(card, saved);
2684|
2685|        evRenderInjuredPersonSummary(card, personId);
2686|
2687|        wrap.appendChild(card);
2688|        evBindInjuredCardEvents(card);
2689|
2690|        if (expand) {
2691|            evExpandInjuredCard(card);
2692|        } else {
2693|            evCollapseInjuredCard(card, true);
2694|            if (evCurrentStep === 'aprofundamento'
2695|                && evSelectedType() === 'ACIDENTE_PESSOAL'
2696|                && !wrap.querySelector('.ev-injured-person-box.is-expanded')) {
2697|                evExpandInjuredCard(card);
2698|            }
2699|        }
2700|        if (evCurrentStep === 'aprofundamento' && evSelectedType() === 'ACIDENTE_PESSOAL') {
2701|            if (typeof syncInjuryFieldsForCard === 'function') {
2702|                syncInjuryFieldsForCard(card);
2703|            }
2704|            if (typeof evFilterCardConsequences === 'function') {
2705|                evFilterCardConsequences(card);
2706|            }
2707|        }
2708|        evEnsurePrimaryInjuredMarked();
2709|        evSyncDescaracterStageUi();
2710|        return card;
2711|    }
2712|
2713|    function evRemoveInjuredPersonBox(personId) {
2714|        var wrap = document.getElementById('ev_injured_person_boxes');
2715|        if (!wrap || !personId) return;
2716|        var safe = String(personId).replace(/"/g, '');
2717|        var card = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2718|        if (!card) return;
2719|        var wasPrimary = card.classList.contains('is-primary');
2720|        if (card.classList.contains('is-expanded')) {
2721|            evParkBodyMapBlock();
2722|            evClearCanonicalInjuryIds();
2723|        }
2724|        card.remove();
2725|        var details = evGetInjuredDetailsObj();
2726|        delete details[String(personId)];
2727|        evSetInjuredDetailsObj(details);
2728|        // Não auto-expande a próxima — todas ficam colapsadas até o usuário editar.
2729|        if (wasPrimary) {
2730|            var next = wrap.querySelector('.ev-injured-person-box[data-person-id]');
2731|            if (next) {
2732|                evSelectPrimaryInjuredPerson(next.getAttribute('data-person-id'));
2733|            } else {
2734|                var sel = document.getElementById('ev_person_id');
2735|                if (sel) sel.removeAttribute('data-primary-injured-id');
2736|                evSyncInjuredPickCircles('');
2737|            }
2738|        } else {
2739|            evSyncPrimaryInjuredPersonId();
2740|        }
2741|        evFilterInjuredPersonSelect();
2742|        evPersistDraftSoon();
2743|        evUpdateInjuredPersonsEmptyState();
2744|    }
2745|
2746|    /**
2747|     * Select oculto (legado): adicionar acidentado manualmente — fluxo principal = cards via Pessoas envolvidas.
2748|     */
2749|    var _evAddingInjuredLock = false;
2750|    function evAddInjuredPersonFromSelect() {
2751|        if (_evAddingInjuredLock) return;
2752|        var sel = document.getElementById('ev_person_id');
2753|        if (!sel) return;
2754|        var personId = String(sel.value || '');
2755|        if (!personId) return;
2756|
2757|        _evAddingInjuredLock = true;
2758|        try {
2759|            var wrap = document.getElementById('ev_injured_person_boxes');
2760|            var safe = personId.replace(/"/g, '');
2761|            var existing = wrap && wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2762|            if (existing) {
2763|                // Mantém colapsada; usuário edita pelo lápis.
2764|                evUpdateInjuredCardSummary(existing);
2765|            } else {
2766|                var saved = evGetInjuredDetailsObj()[personId] || {};
2767|                var created = evCreateInjuredPersonCard(personId, saved, false);
2768|                if (!created) {
2769|                    console.warn('[SSMA] Falha ao criar caixinha do acidentado', personId);
2770|                } else {
2771|                    evSyncInjuredPersonDetailsHidden();
2772|                }
2773|            }
2774|            // Limpa o select sem reentrar (setCustomSelectValue dispara change).
2775|            sel.value = '';
2776|            if (typeof window.setCustomSelectValue === 'function') {
2777|                window.setCustomSelectValue('ev_person_id', '');
2778|            } else {
2779|                evSyncCustomSelectVisual('ev_person_id', true);
2780|            }
2781|            evSyncPrimaryInjuredPersonId();
2782|            evFilterInjuredPersonSelect();
2783|        } finally {
2784|            _evAddingInjuredLock = false;
2785|        }
2786|    }
2787|
2788|    /** Remonta caixinhas a partir de injured_person_details (+ person_id legado). */
2789|    function evRenderInjuredPersonBoxes() {
2790|        var wrap = document.getElementById('ev_injured_person_boxes');
2791|        if (!wrap) return;
2792|        var details = evGetInjuredDetailsObj();
2793|        var ids = Object.keys(details);
2794|        var primary = '';
2795|        var sel = document.getElementById('ev_person_id');
2796|        if (sel && sel.value) primary = String(sel.value);
2797|        if (!primary && sel && sel.getAttribute('data-primary-injured-id')) {
2798|            primary = String(sel.getAttribute('data-primary-injured-id') || '');
2799|        }
2800|
2801|        if (primary && !details[primary]) {
2802|            details[primary] = { attendance_date: evTodayDateInputValue(), had_injury: true, body_parts: [] };
2803|            evSetInjuredDetailsObj(details);
2804|            ids = Object.keys(details);
2805|        }
2806|
2807|        evParkBodyMapBlock();
2808|        evClearCanonicalInjuryIds();
2809|        wrap.innerHTML = '';
2810|
2811|        ids.forEach(function (pid) {
2812|            // Sempre cria colapsada; expandir só via lápis (ou validação).
2813|            evCreateInjuredPersonCard(pid, details[pid] || {}, false);
2814|        });
2815|        // Respeita primary já salvo; senão marca o primeiro.
2816|        var preferred = primary || '';
2817|        if (preferred && wrap.querySelector('.ev-injured-person-box[data-person-id="' + preferred.replace(/"/g, '') + '"]')) {
2818|            evSelectPrimaryInjuredPerson(preferred);
2819|        } else {
2820|            evEnsurePrimaryInjuredMarked();
2821|        }
2822|        evSyncInjuredPersonDetailsHidden();
2823|        evSyncPrimaryInjuredPersonId();
2824|        evFilterInjuredPersonSelect();
2825|        evUpdateInjuredPersonsEmptyState();
2826|        evSyncDescaracterStageUi();
2827|        if (evCurrentStep === 'aprofundamento' && evSelectedType() === 'ACIDENTE_PESSOAL') {
2828|            window.requestAnimationFrame(function () {
2829|                if (typeof evEnsurePrimaryInjuredCardExpanded === 'function') {
2830|                    evEnsurePrimaryInjuredCardExpanded();
2831|                }
2832|            });
2833|        }
2834|    }
2835|    var evDraftTimer = null;
2836|    function evPersistDraftSoon() {
2837|        if (evDraftTimer) clearTimeout(evDraftTimer);
2838|        evDraftTimer = setTimeout(evPersistDraftNow, 400);
2839|    }
2840|    function evPersistDraftNow() {
2841|        var mode = (document.getElementById('ev_form_mode') || {}).value || 'create';
2842|        if (mode !== 'create') return;
2843|        try {
2844|            var form = document.getElementById('form-event-new');
2845|            if (!form) return;
2846|            var data = {};
2847|            Array.prototype.forEach.call(form.elements, function (el) {
2848|                if (!el.name && !el.id) return;
2849|                var key = el.id || el.name;
2850|                if (!key) return;
2851|                if (el.type === 'checkbox' || el.type === 'radio') {
2852|                    data[key] = !!el.checked;
2853|                } else {
2854|                    data[key] = el.value;
2855|                }
2856|            });
2857|            data.__people_ids = (document.getElementById('ev_people_ids') || {}).value || '';
2858|            data.__witness_ids = (document.getElementById('ev_witness_ids') || {}).value || '';
2859|            data.__injured_details = (document.getElementById('ev_injured_person_details') || {}).value || '';
2860|            sessionStorage.setItem(EV_DRAFT_KEY, JSON.stringify(data));
2861|        } catch (e) {}
2862|    }
2863|    function evClearDraft() {
2864|        try { sessionStorage.removeItem(EV_DRAFT_KEY); } catch (e) {}
2865|    }
2866|    function evRestoreDraftIfAny() {
2867|        try {
2868|            var raw = sessionStorage.getItem(EV_DRAFT_KEY);
2869|            if (!raw) return false;
2870|            var data = JSON.parse(raw);
file_read
Show Details
{"file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2900, "end_line": 2980}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2900-2980
2900|            return false;
2901|        }
2902|    }
2903|
2904|    function evCanDescharacterizeAccident() {
2905|        return window.SSMA_CAN_DESCARACTERIZE === true || window.SSMA_CAN_DESCARACTERIZE === 'true';
2906|    }
2907|
2908|    function evIsDescaracterSuspectChecked() {
2909|        var el = document.getElementById('ev_descaracter_suspect');
2910|        return !!(el && el.checked);
2911|    }
2912|
2913|    function evIsCreateMode() {
2914|        return ((document.getElementById('ev_form_mode') || { value: 'create' }).value === 'create');
2915|    }
2916|
2917|    function evSyncDescaracterStageUi() {
2918|        var isAp = evSelectedType() === 'ACIDENTE_PESSOAL';
2919|        var isDoctorAprof = !!evAprofundamentoOnlyMode;
2920|        var form = document.getElementById('form-event-new');
2921|        if (form) form.classList.toggle('is-doctor-aprof', isDoctorAprof);
2922|        // Checkbox global de suspeita (etapa 1) fica sempre oculto — suspeita agora é por card no aprofundamento.
2923|        var suspectWrap = document.getElementById('ev-suspeita-wrap');
2924|        if (suspectWrap) {
2925|            suspectWrap.classList.add('d-none');
2926|        }
2927|        document.querySelectorAll('.ev-inj-descaracter').forEach(function (el) {
2928|            // Seção de descaracterização aparece sempre que é Acidente Pessoal (criação e aprofundamento).
2929|            // Na criação: só o checkbox de suspeita fica visível (Sim/Não oculto via CSS fora do aprofundamento).
2930|            el.classList.toggle('d-none', !isAp);
2931|        });
2932|        // Sincroniza Sim/Não de cada card com o estado do checkbox de suspeita do próprio card.
2933|        document.querySelectorAll('.ev-injured-person-box').forEach(function (card) {
2934|            var suspChk = card.querySelector('.ev-inj-suspect-chk');
2935|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
2936|            if (suspChk && yesNoWrap) {
2937|                yesNoWrap.classList.toggle('d-none', !suspChk.checked);
2938|            }
2939|        });
2940|    }
2941|
2942|    function evSyncDescaracterUi() {
2943|        var hidden = document.getElementById('ev_descaracterizado');
2944|        var canSn = evCanDescharacterizeAccident();
2945|        var cur = String((hidden || {}).value || '');
2946|
2947|        evSyncDescaracterStageUi();
2948|
2949|        // Pré-seleciona "Sim" quando o profissional não tem permissão de descaracterização
2950|        // e o campo ainda está vazio. Ele só pode dizer "Sim" de qualquer forma, então
2951|        // pré-preencher evita a validação falhar sem o usuário perceber o que falta.
2952|        if (!canSn && cur === '' && evAprofundamentoOnlyMode && hidden) {
2953|            hidden.value = '0';
2954|            cur = '0';
2955|            document.querySelectorAll('.ev-injured-person-box').forEach(function (c) {
2956|                c.setAttribute('data-descaracterizado', '0');
2957|            });
2958|        }
2959|
2960|        document.querySelectorAll('.ev-injured-person-box').forEach(function (card) {
2961|            var noPerm = card.querySelector('.ev-inj-descaracter-no-perm');
2962|            var cardDesc = card.getAttribute('data-descaracterizado') || cur;
2963|            if (noPerm) noPerm.classList.toggle('d-none', canSn);
2964|            card.querySelectorAll('.ev-inj-descaracter-btn').forEach(function (b) {
2965|                var val = b.getAttribute('data-descaracter-val');
2966|                // Sem permissão: "Não" fica visível mas desabilitado — o par Sim/Não precisa aparecer.
2967|                if (val === '1' && !canSn) {
2968|                    b.disabled = true;
2969|                    b.classList.remove('d-none');
2970|                    b.setAttribute('title', 'Sem permissão para descaracterizar o acidente.');
2971|                } else {
2972|                    b.disabled = false;
2973|                    b.classList.remove('d-none');
2974|                    b.removeAttribute('title');
2975|                }
2976|            });
2977|            if (window.SsmaShared && typeof window.SsmaShared.toggleYesNo === 'function') {
2978|                window.SsmaShared.toggleYesNo(card.querySelectorAll('.ev-inj-descaracter-btn'), cardDesc !== '' ? cardDesc : null, {
2979|                    valueAttr: 'data-descaracter-val'
2980|                });
Request #3 deepseek-v4-flash P:12.8K C:3.37K CR:8.45K CW:0 23315ms
Reasoning
Now I understand the context. The PR adds calls to `evSyncDescaracterStageUi()` at the end of `evCreateInjuredPersonCard()` (line 2709) and `evRenderInjuredPersonBoxes()` (line 2826). Let me analyze: 1. `evCreateInjuredPersonCard` is called by `evRenderInjuredPersonBoxes` per id in loop. In `evRenderInjuredPersonBoxes`, they also call `evSyncDescaracterStageUi()` at the end (added at line 2826). This might be redundant since `evCreateInjuredPersonCard` already calls `evSyncDescaracterStageUi()` (line 2709). But `evRenderInjuredPersonBoxes` clears the wrap via `wrap.innerHTML = ''` and re-creates cards in a loop. Since each call to `evCreateInjuredPersonCard` now also syncs, the one at line 2826 in `evRenderInjuredPersonBoxes` is redundant but harmless. Actually the intent: the sync after each card creation handles the case where the card appears. Note however, that when the checkbox `suspChk` events fire etc., the per-card Sim/Não sync depends on checkbox state. In creation mode, per-card yes/no is hidden by CSS outside aprofundamento. OK. Potential issue: `evSyncDescaracterStageUi` calls `evSelectedType()` and accesses `.ev-inj-descaracter` elements and `.ev-injured-person-box` cards. In `evCreateInjuredPersonCard`, there's an early-return `if (!wrap || !tpl || !personId) return null;` and `if (existing) return existing;`. The added call at the end only runs after the card is appended. Fine. However, in `evRenderInjuredPersonBoxes`, they call `evParkBodyMapBlock()` and then `wrap.innerHTML = ''`, then loop through ids calling `evCreateInjuredPersonCard`. If no ids exist (empty), `evSyncDescaracterStageUi()` still runs at end. Fine. Now, an important consideration: in the creation of the card, is `evSyncDescaracterStageUi` defined before use? This is a function declaration within a `<script>`; hoisting likely handles. But is the card markup clone from template containing `.ev-inj-descaracter`? Yes. Now, consider the semantics: The comment says the fix syncs checkbox visibility when card is created. The added call to `evSyncDescaracterStageUi()` in `evCreateInjuredPersonCard` is placed after `evEnsurePrimaryInjuredMarked()`. But is the cloned card inside the `.ev-injured-person-box` class? Let me check the template's data element. Let me now check where `evSelectedType` is defined, and whether there's any timing dependency, i.e., functions might be defined later in the same script but function declarations are hoisted within the same `<script>` block. Let me read around lines 3300-3420 where evSyncDescaracterStageUi is also called, to check typical usage. Actually, that's not needed. Let me consider issues: 1. Redundancy: `evCreateInjuredPersonCard` is invoked inside `evRenderInjuredPersonBoxes` loop; the end-of-function call in `evRenderInjuredPersonBoxes` (line 2826) is redundant because each card creation already triggers sync. It is a minor performance/maintainability issue but likely intentional given the "fix" tries to cover both paths. Actually, wait — since each `evCreateInjuredPersonCard` call now runs `evSyncDescaracterStageUi` at end, the call in `evRenderInjuredPersonBoxes` at 2826 becomes a second sync after all cards are built. That might be desired to account for primary marking etc. Not a bug. 2. The important thing is: `evSyncDescaracterStageUi` toggles `.ev-inj-descaracter` visibility only based on whether selected type is ACIDENTE_PESSOAL (`isAp`) — it does NOT depend on the checkbox or any per-card state for the `d-none` toggling of the section. Actually it does: `el.classList.toggle('d-none', !isAp);`. So if type isn't ACIDENTE_PESSOAL the section stays hidden. Wait, but the requirement says in creation mode for Acidente Pessoal, checkbox visible. OK. Hmm, but actually wait. Let's look more carefully. In the `.ev-inj-descaracter` block, there may be a checkbox `.ev-inj-suspect-chk` per card. `evSyncDescaracterStageUi` shows the section for all AP regardless of creation vs aprofundamento, and hides Sim/Não unless checkbox checked (though comment says Sim/Não hidden via CSS outside aprofundamento... actually the code toggles yesNoWrap visibility based on suspChk.checked, regardless of mode). Wait, so on creation, if user checks the checkbox, the Sim/Não buttons would be toggled to show? Hmm. But the code comment says "Na criação: só o checkbox de suspeita fica visível (Sim/Não oculto via CSS fora do aprofundamento)." So maybe there's CSS that hides the yes/no wrap during creation mode. Let me search for CSS rules on `.ev-inj-descaracter-yesno-wrap`. Actually, this is pre-existing logic from #677 and not changed in this PR. We only review the new lines. Let me look at where these new sync calls might cause an issue: consider that `evCreateInjuredPersonCard` is also called during the "aprofundamento" flow. In that case calling evSyncDescaracterStageUi again is fine. Potential real bug: In `evCreateInjuredPersonCard`, at line 2694-2698, when in aprofundamento and the type is AP, card may get expanded. Then at line 2709 `evSyncDescaracterStageUi` runs. Fine. Now, could calling `evSyncDescaracterStageUi` during card creation cause a feedback loop or run before elements referenced exist? No, these are querySelectorAll on the document. One consideration: `evSyncDescaracterStageUi` toggles the form class `is-doctor-aprof` and suspects wrap. Called multiple times — idempotent. Good. But there is a subtle point: after adding the call inside `evCreateInjuredPersonCard`, the `.ev-inj-descaracter` block inside the newly created card will be shown based on current selected type. But what if `evSelectedType()` returns type of selected type select and if it changes later? Then existing handlers call sync at other points (lines 3323, 3398, 5288, 5736). Actually the fix seems correct. Let me verify the function `evSelectedType` and whether within `evCreateInjuredPersonCard`, when the type isn't ACIDENTE_PESSOAL yet the section should remain hidden. The new call would hide section for non-AP types. Fine. Now let's check whether there might be a case where the type change happens AFTER the card creation in normal flow. In the UI, the user first chooses the type (Acidente com Pessoa) and then adds person. Actually the sequence in the flow is that people are added before choosing the type? Let's read the flow — adding a person requires the type to be chosen maybe. Anyway the fix ensures sync after adding the card. Potential double `evSyncDescaracterStageUi` in `evRenderInjuredPersonBoxes`: since each `evCreateInjuredPersonCard` already calls it, the extra call at line 2826 is redundant. But redundancy isn't harmful; it's idempotent. Could mention as minor. Actually, hmm, wait: is the call at 2826 meant to cover the "wrap.innerHTML = ''" when there are 0 people? If no ids, no card is created, and the extra call ensures section state is updated — but that's probably needed anyway. Not a problem. Let me check the diff more carefully in _modal_event. The added lines are exactly: - line 2709: `evSyncDescaracterStageUi();` - line 2826: `evSyncDescaracterStageUi();` Now let me analyze potential issue of the call in `evCreateInjuredPersonCard` in the "existing card" early return case (line 2658-2659): returns existing card without syncing. So if the existing card is shown (e.g., after it was hidden), the sync won't run, but other code paths probably handle that (e.g., when re-showing card). That is not introduced here. Potential regression concern in aprofundamento (the PR's risk): The function toggles `.ev-inj-descaracter` visibility when `isAp` true, independent of aprofundamento mode. Since previously this only ran upon type change; now it runs whenever a card is added/rendered. In aprofundamento editing, adding a new injured person card via evCreateInjuredPersonCard now triggers a sync that toggles `yesNoWrap` visibility based on the checkbox state. That's probably fine and consistent with existing behavior. However there is one subtle issue: The `evSyncDescaracterStageUi` calls `form.classList.toggle('is-doctor-aprof', isDoctorAprof)` where `isDoctorAprof = !!evAprofundamentoOnlyMode`. `evAprofundamentoOnlyMode` is presumably a variable defined elsewhere in the script. Fine. Now let me consider whether the added call inside `evCreateInjuredPersonCard` could cause XSS or such? No. Let me also check where else `evCreateInjuredPersonCard` is called from, e.g., validation flows, editing existing occurrences in aprofundamento (which reloads with pre-existing cards). Also, `evRenderInjuredPersonBoxes` runs when reopening modal in aprofundamento. In the aprofundamento case, if the type is not yet selected (evSelectedType returns ''?), the section would get hidden. But when editing an existing occurrence in aprofundamento mode, the type should be selected/preset before boxes render. Probably fine. Let me consider a possible issue: There might be a scenario where `evCreateInjuredPersonCard` is called when `evSelectedType()` is not yet ACIDENTE_PESSOAL but will be later — e.g., when user adds a person first and then picks the type. Let's look at the flow: Adding a person from "Pessoas envolvidas" requires? Let's search for how adding person and type interact. Let me look at where evRenderInjuredPersonBoxes is called and evSelectedType. Let me also verify the new call isn't inserted inside a block where `evSyncDescaracterStageUi` is referenced before its definition in a way that breaks (function declaration order). Within the same <script> (or possibly multiple script blocks?) Function declarations are hoisted within the scope. If it's the same script element, fine. Let me search for the definition location of `evSyncDescaracterStageUi` relative to the enclosing script. Line 2917 is after line 2709 but within the same script? Need to check that the two functions are in the same <script> block. It's likely a giant script. Function declarations are hoisted to top of the containing function/script scope; calls at runtime will work as long as script parsed. Fine. Now the JSON file `.opencodereview/rule.json` — added two new rules entries. The keys are "path", "merge_system_rule", "rule". Check spelling of keys: "path", "merge_system_rule", "rule" — all fine. Values content ignored. New rule file `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` referenced; note this file was changed but not in review group (the sample says md=1 changed). Not in review_files, so ignore per instructions. The rule JSON: added two entries mapping the template paths to rule. They're consistent with other entries. Now what about review of the twig template with the user-specific rules: "God template" - This template is already huge (7712 lines). The PR adds only 2 lines within existing giant script. Since the diff adds only 2 lines to an existing god-template, per the priority rule, we should flag that logic is embedded in a giant template with `<script>` blocks instead of `public/js/`. But is it appropriate to raise on a 2-line fix? The rules say "Se este template já é um 'god template', qualquer PR que aumente ainda mais essa mistura deve ser sinalizada antes de qualquer outro achado." So yes we should raise attention/flag. However, given the 2-line addition merely adds function calls, it doesn't really increase the mixing much. But per rule, we may give a "Leve/Atenção" note. Hmm. We need to be careful and concise. The two new lines call an existing function already defined in that template script. So arguably it's consistent with existing structure. We can mention it as a low-severity maintainability note, maybe. Now let me focus on actual possible bugs. Check for `evCreateInjuredPersonCard` return path when person already exists: At line 2658-2659: ``` var existing = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]'); if (existing) return existing; ``` The added sync line runs only on new cards, not when an existing card is returned. Not a new bug. Consider `evRenderInjuredPersonBoxes`: it clears `wrap.innerHTML=''` and then loops; each call to evCreateInjuredPersonCard triggers sync (line 2709). Then at the end, line 2826 calls sync again. So we have double sync, but harmless. But wait—the sequence in evRenderInjuredPersonBoxes: it calls `evSelectPrimaryInjuredPerson(preferred)` before line 2826 which may expand cards... fine. Now let's think about `evSyncDescaracterStageUi` behavior in relation to per-card yes/no wrap in creation mode. In creation mode, `.ev-inj-suspect-chk` may not exist yet (only in aprofundamento?), and the Sim/Não hiding relies on CSS. Let me look at the markup for `.ev-inj-descaracter` and `.ev-inj-suspect-chk`. Let me search in the file for `ev-inj-descaracter` and `ev-inj-suspect-chk`. Let me read the template markup of the card (probably defined inside `<template id="ev-injured-person-box-tpl">`). Let me search.
Tool Calls (2)
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "ev-inj-descaracter"}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 32
1289|#form-event-new .ev-inj-descaracter-toggle {
1292|#form-event-new .ev-inj-descaracter-btn.active {
1297|#form-event-new .ev-inj-descaracter-btn[disabled] {
1301|#form-event-new .ev-inj-descaracter {
1305|#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle,
1306|#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-comment-wrap,
1307|#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-no-perm {
1310|#form-event-new .ev-inj-descaracter-toggle.is-invalid {
1392|.ev-step-panel.is-readonly .ev-inj-descaracter-btn {
1396|#form-event-new .ev-inj-descaracter-suspect-ro {
1401|#form-event-new .ev-inj-descaracter-suspect-ro-wrap .form-check-label {
2201|            descaracter_comment: (card.querySelector('.ev-inj-descaracter-comment') || {}).value || '',
2208|            var cardBtns = card.querySelectorAll('.ev-inj-descaracter-btn.active');
2257|        var comm = card.querySelector('.ev-inj-descaracter-comment');
2598|        card.querySelectorAll('.ev-inj-attendance, .ev-inj-relato, .ev-inj-injury-type, .ev-inj-injury-severity, .ev-inj-injury-classification, .ev-inj-had-injury, .ev-inj-consequence, .ev-inj-potential-consequence, .ev-inj-descaracter-comment').forEach(function (el) {
2624|        card.querySelectorAll('.ev-inj-descaracter-btn').forEach(function (btn) {
2633|                var toggle = card.querySelector('.ev-inj-descaracter-toggle');
2640|        card.querySelectorAll('.ev-inj-descaracter-comment').forEach(function (ta) {
2649|            window.jQuery(card).find('.ev-inj-descaracter-tip').tooltip({ container: 'body' });
2927|        document.querySelectorAll('.ev-inj-descaracter').forEach(function (el) {
2935|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
2961|            var noPerm = card.querySelector('.ev-inj-descaracter-no-perm');
2964|            card.querySelectorAll('.ev-inj-descaracter-btn').forEach(function (b) {
2978|                window.SsmaShared.toggleYesNo(card.querySelectorAll('.ev-inj-descaracter-btn'), cardDesc !== '' ? cardDesc : null, {
3314|            '#ev-step-aprofundamento .ev-inj-descaracter-btn'
3318|            if (el.classList && el.classList.contains('ev-inj-descaracter-suspect-ro')) return;
5746|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
6026|                    var toggle = card.querySelector('.ev-inj-descaracter-toggle');
6027|                    var comm = card.querySelector('.ev-inj-descaracter-comment');
6059|                            || (focusCard && focusCard.querySelector('.ev-inj-descaracter'))
6487|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
7121|                ? String((primaryCard.querySelector('.ev-inj-descaracter-comment') || {}).value || '').trim()

code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "evCreateInjuredPersonCard"}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 4
2378|                evCreateInjuredPersonCard(personId, saved, false);
2653|    function evCreateInjuredPersonCard(personId, saved, expand) {
2767|                var created = evCreateInjuredPersonCard(personId, saved, false);
2813|            evCreateInjuredPersonCard(pid, details[pid] || {}, false);

Request #4 deepseek-v4-flash P:17.11K C:287 CR:16.13K CW:0 2500ms
Reasoning
Let me examine the other call sites of evSyncDescaracterStageUi: line 3323, 3398, 5288, 5736 - to understand when they're invoked. And the markup for `.ev-inj-descaracter` — inside the card template. Let me read around line 5736-5760 for the yesNoWrap logic and around 5288. Also look at line 1289-1310 CSS to understand the `.is-doctor-aprof` toggle which handles visibility.
Tool Calls (2)
file_read
Show Details
{"file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "end_line": 1310, "start_line": 1285}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 1285-1310
1285|    font-size: 1rem;
1286|    line-height: 1.5;
1287|    box-sizing: border-box;
1288|}
1289|#form-event-new .ev-inj-descaracter-toggle {
1290|    gap: 8px;
1291|}
1292|#form-event-new .ev-inj-descaracter-btn.active {
1293|    background: #1a6e7f !important;
1294|    color: #fff !important;
1295|    border-color: #1a6e7f !important;
1296|}
1297|#form-event-new .ev-inj-descaracter-btn[disabled] {
1298|    opacity: 0.45;
1299|    cursor: not-allowed;
1300|}
1301|#form-event-new .ev-inj-descaracter {
1302|    padding-bottom: 4px;
1303|}
1304|/* Sim/Não de descaracterização fica oculto fora do aprofundamento — só o checkbox de suspeita aparece na criação. */
1305|#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle,
1306|#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-comment-wrap,
1307|#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-no-perm {
1308|    display: none !important;
1309|}
1310|#form-event-new .ev-inj-descaracter-toggle.is-invalid {
file_read
Show Details
{"file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "end_line": 3400, "start_line": 3300}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 3300-3400
3300|        if (panel) panel.classList.toggle('is-readonly', !!readonly);
3301|        if (alert) {
3302|            alert.classList.toggle('d-none', !readonly);
3303|            if (evIsAprofundamentoFinalized() && !EV_IS_ADMIN_APROFUNDAMENTO) {
3304|                alert.innerHTML = '<i class="fas fa-lock mr-2"></i>Aprofundamento finalizado. Somente um administrador ou gestor administrador pode alterar.';
3305|            } else {
3306|                alert.innerHTML = '<i class="fas fa-exclamation-triangle mr-2"></i>O aprofundamento é preenchido pelo profissional responsável (gestor direto do colaborador ou equipe técnica SSMA).';
3307|            }
3308|        }
3309|        var controls = document.querySelectorAll(
3310|            '#ev-technical-section input, #ev-technical-section select, #ev-technical-section textarea,' +
3311|            '#ev-spec-type-card input, #ev-spec-type-card select, #ev-spec-type-card textarea,' +
3312|            '#ev-gravity-wrap input, #ev-gravity-wrap select,' +
3313|            '#ev-corrective-actions-section input, #ev-corrective-actions-section select, #ev-corrective-actions-section textarea, #ev-corrective-actions-section button,' +
3314|            '#ev-step-aprofundamento .ev-inj-descaracter-btn'
3315|        );
3316|        Array.prototype.forEach.call(controls, function (el) {
3317|            if (!el || el.type === 'hidden') return;
3318|            if (el.classList && el.classList.contains('ev-inj-descaracter-suspect-ro')) return;
3319|            el.disabled = !!readonly;
3320|        });
3321|        // Reaplica bloqueio Sem dano — o loop acima zera disabled de todos os selects.
3322|        evSyncInjuryTypeByConsequence();
3323|        evSyncDescaracterStageUi();
3324|    }
3325|
3326|    function evUpdateFooter() {
3327|        var type = evSelectedType();
3328|        var canEditAprofundamento = evCanEditAprofundamento(type);
3329|        var back = document.getElementById('ev-btn-back');
3330|        var cancel = document.getElementById('ev-btn-cancel');
3331|        var save = document.getElementById('ev-btn-save');
3332|        var draft = document.getElementById('ev-btn-draft');
3333|        var label = document.getElementById('ev-btn-label');
3334|        if (draft) {
3335|            draft.classList.toggle('d-none', !evAprofundamentoOnlyMode || evCurrentStep !== 'aprofundamento');
3336|        }
3337|        if (evAprofundamentoOnlyMode) {
3338|            if (back) back.classList.add('d-none');
3339|            if (cancel) cancel.classList.remove('d-none');
3340|            if (!label || !save) return;
3341|            if (evIsAprofundamentoFinalized() && !EV_IS_ADMIN_APROFUNDAMENTO) {
3342|                save.disabled = true;
3343|                save.classList.add('d-none');
3344|                if (draft) draft.classList.add('d-none');
3345|                return;
3346|            }
3347|            save.classList.remove('d-none');
3348|            save.disabled = false;
3349|            label.textContent = evIsAprofundamentoFinalized() ? 'Salvar alterações' : 'Finalizar';
3350|            return;
3351|        }
3352|        if (back) back.classList.toggle('d-none', evCurrentStep !== 'aprofundamento');
3353|        if (cancel) cancel.classList.toggle('d-none', evCurrentStep === 'aprofundamento');
3354|        if (!label || !save) return;
3355|        save.classList.remove('d-none');
3356|        save.disabled = false;
3357|        if (evCurrentStep === 'general') {
3358|            label.innerHTML = (evRequiresAprofundamento(type) && evCanEditAprofundamento(type))
3359|                ? 'Aprofundamento <i class="fas fa-chevron-right ml-1"></i>'
3360|                : ((document.getElementById('ev_form_mode') || { value: 'create' }).value === 'edit'
3361|                    ? 'Salvar alterações'
3362|                    : 'Registrar');
3363|            return;
3364|        }
3365|        label.textContent = (document.getElementById('ev_form_mode') || { value: 'create' }).value === 'edit'
3366|            ? 'Salvar alterações'
3367|            : 'Registrar';
3368|        // Registrar nunca fica bloqueado por falta de permissão de aprofundamento.
3369|        save.disabled = false;
3370|    }
3371|
3372|    function evSetStep(step) {
3373|        if (evAprofundamentoOnlyMode) {
3374|            step = 'aprofundamento';
3375|        }
3376|        evCurrentStep = step === 'aprofundamento' ? 'aprofundamento' : 'general';
3377|        var general = document.getElementById('ev-step-general');
3378|        var apro = document.getElementById('ev-step-aprofundamento');
3379|        if (general) {
3380|            general.classList.toggle('d-none', evCurrentStep !== 'general');
3381|            general.classList.toggle('is-readonly', !!evAprofundamentoOnlyMode);
3382|        }
3383|        if (apro) apro.classList.toggle('d-none', evCurrentStep !== 'aprofundamento');
3384|        document.querySelectorAll('#ev-steps-bar .insp-step-seg').forEach(function (bar) {
3385|            var key = bar.getAttribute('data-ev-progress');
3386|            if (evAprofundamentoOnlyMode) {
3387|                bar.classList.toggle('is-active', key === 'aprofundamento');
3388|                bar.classList.toggle('active', key === 'aprofundamento');
3389|            } else {
3390|                bar.classList.toggle('is-active', key === 'general' || key === evCurrentStep);
3391|                bar.classList.toggle('active', key === 'general' || key === evCurrentStep);
3392|            }
3393|        });
3394|        var body = document.querySelector('#modalEventNew-offcanvas-wrapper .offcanvas-body');
3395|        if (body) body.scrollTop = 0;
3396|        evSetAprofundamentoReadonly(!evCanEditAprofundamento(evSelectedType()));
3397|        evUpdateFooter();
3398|        evSyncDescaracterStageUi();
3399|        if (evCurrentStep === 'aprofundamento') {
3400|            var stepType = evSelectedType();
Request #5 deepseek-v4-flash P:19.44K C:748 CR:17.28K CW:0 5734ms
Reasoning
The change looks correct and minimal. The concern points: 1. `evCreateInjuredPersonCard` is called inside `evRenderInjuredPersonBoxes`, so adding `evSyncDescaracterStageUi()` at line 2709 already covers the render path. Adding another call at line 2826 is redundant (double call). Minor. 2. Actually, there's a subtle consideration: is there a case where the sync is needed at 2826 because of cards expanded and checked? Not really necessary. 3. God template concern: The rule says this is a "god template" — any PR that increases mixing should be flagged first. But this PR only adds two calls to an existing function defined in the same script. It doesn't add new markup/logic. So is it worth flagging? The rule says flag it before any other finding. But the change is 2 lines and is a bug fix of prior PR. However, per review instructions "Avoid commenting on correct code." We can raise the god-template as a low/info note maybe. But I need to be objective and concise, not manufacture issues. Let me consider more carefully whether the added line in `evCreateInjuredPersonCard` (line 2709) could cause an actual behavior regression. `evSyncDescaracterStageUi` toggles `yesNoWrap` visibility based on `suspChk.checked` for every card. Also toggles `.ev-inj-descaracter` d-none based on type. Important: In creation mode (not doctor-aprof), CSS hides Sim/Não. The function's per-card toggle for yesNoWrap... let's check: CSS `#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle` display none !important. So fine. Now consider `evRemoveInjuredPersonBox` — does not call evSyncDescaracterStageUi. Not part of diff. Potential bug with re-render while `evAprofundamentoOnlyMode` true: When evRenderInjuredPersonBoxes runs in aprofundamento mode, each evCreateInjuredPersonCard triggers a sync. Fine. Wait: could there be a case where `evCreateInjuredPersonCard` is invoked when a new person card is being created but the type hasn't been set (e.g., modal initial load with a pre-saved type ''?). evSelectedType returns value of select. If empty, then section hidden — but cards presumably only added when type is AP? In flow, adding people requires type selected? Let me check where "Pessoas envolvidas" add happens (line 2378 call). Let me read around 2350-2400.
Tool Calls (2)
file_read
Show Details
{"end_line": 2400, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2330}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2330-2400
2330|    }
2331|
2332|    /** Texto dinâmico: "N acidentado(s) — o painel contabiliza N ocorrências" (Brenda). */
2333|    function evUpdateInjuredPersonsSummary(count) {
2334|        var summary = document.getElementById('ev-injured-person-summary');
2335|        var summaryText = document.getElementById('ev-injured-person-summary-text');
2336|        if (!summary || !summaryText) return;
2337|        if (!count || count < 1) {
2338|            summary.classList.add('d-none');
2339|            return;
2340|        }
2341|        var pessoaLabel = count === 1 ? 'acidentado registrado' : 'acidentados registrados';
2342|        var ocorrenciaLabel = count === 1 ? 'ocorrência' : 'ocorrências';
2343|        summaryText.textContent = count + ' ' + pessoaLabel + ' — o painel contabiliza ' + count + ' ' + ocorrenciaLabel + '.';
2344|        summary.classList.remove('d-none');
2345|    }
2346|
2347|    function evSyncInjuredCardsFromInvolved() {
2348|        if (evSelectedType() !== 'ACIDENTE_PESSOAL') {
2349|            evUpdateInjuredPersonsEmptyState();
2350|            return;
2351|        }
2352|        var involved = evGetPeopleInvolved();
2353|        var wrap = document.getElementById('ev_injured_person_boxes');
2354|        if (!involved.ids.length) {
2355|            // Nunca apagar caixinhas médicas já montadas / details salvos só porque as tags
2356|            // de "Pessoas envolvidas" ainda não estão no DOM (bug em edição → parte médica some).
2357|            var existingDetails = evGetInjuredDetailsObj();
2358|            var hasCards = !!(wrap && wrap.querySelector('.ev-injured-person-box[data-person-id]'));
2359|            if (hasCards || Object.keys(existingDetails || {}).length) {
2360|                evUpdateInjuredPersonsEmptyState();
2361|                return;
2362|            }
2363|            if (wrap) wrap.innerHTML = '';
2364|            evSetInjuredDetailsObj({});
2365|            if (typeof evParkBodyMapBlock === 'function') evParkBodyMapBlock();
2366|            if (typeof evClearCanonicalInjuryIds === 'function') evClearCanonicalInjuryIds();
2367|            var sel = document.getElementById('ev_person_id');
2368|            if (sel) sel.removeAttribute('data-primary-injured-id');
2369|            evUpdateInjuredPersonsEmptyState();
2370|            return;
2371|        }
2372|        involved.ids.forEach(function (personId) {
2373|            if (!personId || !wrap) return;
2374|            var safe = String(personId).replace(/"/g, '');
2375|            var existing = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2376|            if (!existing) {
2377|                var saved = evGetInjuredDetailsObj()[personId] || {};
2378|                evCreateInjuredPersonCard(personId, saved, false);
2379|            } else {
2380|                evRenderInjuredPersonSummary(existing, existing.getAttribute('data-person-id'));
2381|                evUpdateInjuredCardSummary(existing);
2382|            }
2383|        });
2384|        evPruneInjuredBoxesNotInvolved();
2385|        evEnsurePrimaryInjuredMarked();
2386|        evSyncInjuredPersonDetailsHidden();
2387|        evUpdateInjuredPersonsEmptyState();
2388|        var boxesWrap = document.getElementById('ev_injured_person_boxes');
2389|        if (boxesWrap) boxesWrap.classList.remove('is-invalid');
2390|    }
2391|
2392|    /** No passo Aprofundamento (AP), expande o acidentado principal para exibir lesão/consequências/mapa. */
2393|    function evEnsurePrimaryInjuredCardExpanded() {
2394|        if (evSelectedType() !== 'ACIDENTE_PESSOAL') return null;
2395|        evSyncInjuredCardsFromInvolved();
2396|        var wrap = document.getElementById('ev_injured_person_boxes');
2397|        if (!wrap) return null;
2398|        var card = (typeof evGetPrimaryInjuredCard === 'function' ? evGetPrimaryInjuredCard() : null)
2399|            || wrap.querySelector('.ev-injured-person-box[data-person-id]');
2400|        if (!card) return null;
file_read Failed
Show Details
{"end_line": 2960, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 5260}
Request #6 deepseek-v4-flash P:21.37K C:228 CR:20.1K CW:0 1830ms
Reasoning
Let me read around 5288 and 5736 separately (the other call sites) to understand if evSyncDescaracterStageUi is invoked from event handlers on the checkboxes; also to see how the suspect checkbox is wired.
Tool Calls (2)
file_read
Show Details
{"end_line": 5300, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 5270}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 5270-5300
5270|        var isQa = type === 'QUASE_ACIDENTE';
5271|        if (wrap) {
5272|            wrap.classList.add('d-none');
5273|        }
5274|        sel.required = false;
5275|        if (derivedWrap) derivedWrap.classList.toggle('d-none', !isAccident);
5276|        if (rosDerivedWrap) rosDerivedWrap.classList.toggle('d-none', !isRos);
5277|        if (qaDerivedWrap) qaDerivedWrap.classList.toggle('d-none', !isQa);
5278|        if (isAccident) {
5279|            evSyncDerivedSeverityFromConsequence();
5280|        } else if (isRos) {
5281|            evSyncRosDerivedSeverityFromPotential();
5282|        } else if (isQa) {
5283|            evSyncQaDerivedSeverityFromPotential();
5284|        }
5285|        evSyncAaIdentFields(type);
5286|        evSyncPeopleFieldForType(type);
5287|        evSyncReportedByFieldForType(type);
5288|        evSyncDescaracterStageUi();
5289|    }
5290|
5291|    function evSyncPeopleFieldForType(type) {
5292|        var wrap = document.getElementById('ev-people-field-wrap');
5293|        if (!wrap) return;
5294|        var hide = type === 'ROS';
5295|        wrap.classList.toggle('d-none', hide);
5296|        if (hide) {
5297|            var peopleIds = document.getElementById('ev_people_ids');
5298|            if (peopleIds) peopleIds.value = '';
5299|            var tags = document.getElementById('ev_people_tags');
5300|            if (tags) tags.innerHTML = '';
file_read
Show Details
{"end_line": 5760, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 5720}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 5720-5760
5720|            potEl.addEventListener('change', evPersistDraftSoon);
5721|        }
5722|        var derivedSel = document.getElementById('ev_derived_criticality_select');
5723|        if (derivedSel) {
5724|            derivedSel.addEventListener('change', function () {
5725|                var hidden = document.getElementById('ev_derived_criticality');
5726|                var critSel = document.getElementById('ev_criticality');
5727|                if (hidden) hidden.value = this.value || '';
5728|                if (critSel && this.value) critSel.value = this.value;
5729|                evPersistDraftSoon();
5730|            });
5731|        }
5732|        // Descaracterização: suspeita na 1ª etapa; caracterizado na caixinha (aprofundamento).
5733|        var suspectChk = document.getElementById('ev_descaracter_suspect');
5734|        if (suspectChk) {
5735|            suspectChk.addEventListener('change', function () {
5736|                evSyncDescaracterStageUi();
5737|                evSyncInjuredPersonDetailsHidden();
5738|                evPersistDraftSoon();
5739|            });
5740|        }
5741|        // Checkbox de suspeita por card (aprofundamento): mostra/oculta Sim/Não do próprio card.
5742|        document.addEventListener('change', function (e) {
5743|            if (!e.target || !e.target.classList.contains('ev-inj-suspect-chk')) return;
5744|            var card = e.target.closest('.ev-injured-person-box');
5745|            if (!card) return;
5746|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
5747|            if (yesNoWrap) yesNoWrap.classList.toggle('d-none', !e.target.checked);
5748|            // Atualiza o data-descaracter-suspect do card para serialização.
5749|            card.setAttribute('data-descaracter-suspect', e.target.checked ? '1' : '0');
5750|            // Espelha no campo global usado pelo payload (evIsDescaracterSuspectChecked).
5751|            var globalChk = document.getElementById('ev_descaracter_suspect');
5752|            if (globalChk) globalChk.checked = e.target.checked;
5753|            evSyncInjuredPersonDetailsHidden();
5754|            evPersistDraftSoon();
5755|        });
5756|        var contDone = document.getElementById('ev_containment_done');
5757|        if (contDone) {
5758|            contDone.addEventListener('change', function () {
5759|                evSyncContainmentTimeEnabled();
5760|                evPersistDraftSoon();
Request #7 deepseek-v4-flash P:22.75K C:299 CR:21.5K CW:0 2493ms
Reasoning
Now let's look at markup for card, to see where `.ev-inj-descaracter` sits inside the card and where `.ev-inj-suspect-chk` is. This is important: if `.ev-inj-suspect-chk` checkbox only exists in the aprofundamento "readonly" mode markup vs creation markup, then whether evSyncDescaracterStageUi shows the section in creation depends on the presence of `.ev-inj-descaracter` element in cloned card template. Let me read the template portion of the card. Search for `ev-inj-suspect-chk` and `ev-injured-person-box-tpl`.
Tool Calls (2)
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "ev-inj-suspect-chk"}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 3
2934|            var suspChk = card.querySelector('.ev-inj-suspect-chk');
5743|            if (!e.target || !e.target.classList.contains('ev-inj-suspect-chk')) return;
6484|            var chk = card.querySelector('.ev-inj-suspect-chk');

code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "ev-injured-person-box-tpl"}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 2
662|            <div id="ev-injured-person-box-tpl" class="d-none" aria-hidden="true">
2655|        var tpl = document.getElementById('ev-injured-person-box-tpl');

Request #8 deepseek-v4-flash P:23.23K C:138 CR:23.04K CW:0 1449ms
Reasoning
Let me read the markup of the card template around line 662 to see `.ev-inj-descaracter`, and around line 6484 context of per-card suspect checkbox in aprofundamento read-only etc.
Tool Calls (1)
file_read
Show Details
{"end_line": 900, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 660}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 660-900
660|            <input type="hidden" id="ev_injured_person_details" name="ev_injured_person_details" value="">
661|            {# Fonte para clone (div oculta — mais confiável que <template> no offcanvas) #}
662|            <div id="ev-injured-person-box-tpl" class="d-none" aria-hidden="true">
663|                {% include 'ssma/occurrence/partials/_ev_injured_person_box.html.twig' with {
664|                    person_id: '',
665|                    person_name: '',
666|                    attendance_date: '',
667|                    breve_relato: ''
668|                } %}
669|            </div>
670|
671|            {# Body map fica estacionado aqui e é movido para dentro da caixinha do acidentado #}
672|            <div id="ev-body-map-park" class="d-none" aria-hidden="true">
673|            <div id="ev-body-map-block" class="d-none mt-2 ev-ap-body-map-field">
674|                <p class="mb-1 ev-ap-body-map-title">Partes do corpo</p>
675|                <div id="ev-body-map-wrap" class="w-100">
676|                    <div id="ev-body-map-host" class="ssma-ev-body-map-host d-flex justify-content-center mb-3"></div>
677|                    {# Um float por lado (mão esq/dir, pé esq/dir) — posição via JS (data-region no SVG) #}
678|                    <div id="ev_extremity_hand_float_esq" class="ev-extremity-float d-none" aria-hidden="true">
679|                        <div class="ev-ef-label">Mão esq. <span class="ev-ef-subtitle">dedo(s)</span></div>
680|                        <div class="ev-ef-checks">
681|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="POLEGAR"> Polegar</label>
682|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="INDICADOR"> Indicador</label>
683|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MEDIO"> Dedo médio</label>
684|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="ANULAR"> Anelar</label>
685|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MINIMO"> Mínimo</label>
686|                        </div>
687|                        <input type="hidden" id="ev_hand_finger_esq" name="ev_hand_finger_esq" value="">
688|                    </div>
689|                    <div id="ev_extremity_hand_float_dir" class="ev-extremity-float d-none" aria-hidden="true">
690|                        <div class="ev-ef-label">Mão dir. <span class="ev-ef-subtitle">dedo(s)</span></div>
691|                        <div class="ev-ef-checks">
692|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="POLEGAR"> Polegar</label>
693|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="INDICADOR"> Indicador</label>
694|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MEDIO"> Dedo médio</label>
695|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="ANULAR"> Anelar</label>
696|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MINIMO"> Mínimo</label>
697|                        </div>
698|                        <input type="hidden" id="ev_hand_finger_dir" name="ev_hand_finger_dir" value="">
699|                    </div>
700|                    <div id="ev_extremity_foot_float_esq" class="ev-extremity-float d-none" aria-hidden="true">
701|                        <div class="ev-ef-label">Pé esq. <span class="ev-ef-subtitle">dedo(s)</span></div>
702|                        <div class="ev-ef-checks">
703|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="HALUX"> Hálux</label>
704|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="SEGUNDO_DEDO"> 2º dedo</label>
705|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="TERCEIRO_DEDO"> 3º dedo</label>
706|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="QUARTO_DEDO"> 4º dedo</label>
707|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MINIMO_PE"> Mínimo</label>
708|                        </div>
709|                        <input type="hidden" id="ev_foot_toe_esq" name="ev_foot_toe_esq" value="">
710|                    </div>
711|                    <div id="ev_extremity_foot_float_dir" class="ev-extremity-float d-none" aria-hidden="true">
712|                        <div class="ev-ef-label">Pé dir. <span class="ev-ef-subtitle">dedo(s)</span></div>
713|                        <div class="ev-ef-checks">
714|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="HALUX"> Hálux</label>
715|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="SEGUNDO_DEDO"> 2º dedo</label>
716|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="TERCEIRO_DEDO"> 3º dedo</label>
717|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="QUARTO_DEDO"> 4º dedo</label>
718|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MINIMO_PE"> Mínimo</label>
719|                        </div>
720|                        <input type="hidden" id="ev_foot_toe_dir" name="ev_foot_toe_dir" value="">
721|                    </div>
722|                    <div id="ev_zone_cabeca_float" class="ev-extremity-float ev-zone-float d-none" aria-hidden="true">
723|                        <div class="ev-ef-label">Cabeça <span class="ev-ef-subtitle">detalhe</span></div>
724|                        <div class="ev-ef-checks ev-ef-checks--grid">
725|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="OLHO_ESQ"> Olho esq.</label>
726|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="OLHO_DIR"> Olho dir.</label>
727|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="BOCA"> Boca</label>
728|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="NARIZ"> Nariz</label>
729|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="ORELHA_ESQ"> Orelha esq.</label>
730|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="ORELHA_DIR"> Orelha dir.</label>
731|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="TESTA"> Testa</label>
732|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="MENTO"> Queixo</label>
733|                        </div>
734|                        <input type="hidden" id="ev_cabeca_zones" name="ev_cabeca_zones" value="">
735|                    </div>
736|                    <div id="ev_zone_pescoco_float" class="ev-extremity-float ev-zone-float d-none" aria-hidden="true">
737|                        <div class="ev-ef-label">Pescoço <span class="ev-ef-subtitle">detalhe</span></div>
738|                        <div class="ev-ef-checks">
739|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="PESCOCO_ANT"> À frente</label>
740|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="PESCOCO_POST"> Nuca</label>
741|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="PESCOCO_LAT_ESQ"> Lado esq.</label>
742|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="PESCOCO_LAT_DIR"> Lado dir.</label>
743|                        </div>
744|                        <input type="hidden" id="ev_pescoco_zones" name="ev_pescoco_zones" value="">
745|                    </div>
746|                    <div id="ev_zone_face_float" class="ev-extremity-float ev-zone-float d-none" aria-hidden="true">
747|                        <div class="ev-ef-label">Face <span class="ev-ef-subtitle">detalhe</span></div>
748|                        <div class="ev-ef-checks ev-ef-checks--grid">
749|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="FACE_BOCA"> Boca</label>
750|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="FACE_NARIZ"> Nariz</label>
751|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="FACE_BOCHE_ESQ"> Bochecha esq.</label>
752|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="FACE_BOCHE_DIR"> Bochecha dir.</label>
753|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="FACE_MENTO"> Mento</label>
754|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="FACE_MAXILAR"> Maxilar</label>
755|                        </div>
756|                        <input type="hidden" id="ev_face_zones" name="ev_face_zones" value="">
757|                    </div>
758|                    <div id="ev_zone_olhos_float" class="ev-extremity-float ev-zone-float d-none" aria-hidden="true">
759|                        <div class="ev-ef-label">Olhos <span class="ev-ef-subtitle">detalhe</span></div>
760|                        <div class="ev-ef-checks">
761|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="OLHO_ESQ"> Olho esq.</label>
762|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="OLHO_DIR"> Olho dir.</label>
763|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="SOBRANCELHA_ESQ"> Sobrancelha esq.</label>
764|                            <label class="ev-ef-check-item"><input type="checkbox" class="ev-ef-cb" value="SOBRANCELHA_DIR"> Sobrancelha dir.</label>
765|                        </div>
766|                        <input type="hidden" id="ev_olhos_zones" name="ev_olhos_zones" value="">
767|                    </div>
768|                </div>
769|                <div class="form-group mb-0">
770|                    <select id="ev_body_region_select" class="d-none ssma-tag-engine-select" tabindex="-1" aria-hidden="true">
771|                        <option value="">Selecione a região</option>
772|                        <option value="CABECA">Cabeça</option>
773|                        <option value="OLHOS">Olhos</option>
774|                        <option value="FACE">Face</option>
775|                        <option value="PESCOCO">Pescoço</option>
776|                        <option value="OMBRO_ESQ">Ombro esquerdo</option>
777|                        <option value="OMBRO_DIR">Ombro direito</option>
778|                        <option value="OMBRO">Ombro (ambos — legado)</option>
779|                        <option value="BRACO_ESQ">Braço esquerdo</option>
780|                        <option value="BRACO_DIR">Braço direito</option>
781|                        <option value="BRACO">Braço (ambos — legado)</option>
782|                        <option value="ANTEBRACO_ESQ">Antebraço esquerdo</option>
783|                        <option value="ANTEBRACO_DIR">Antebraço direito</option>
784|                        <option value="ANTEBRACO">Antebraço (ambos — legado)</option>
785|                        <option value="MAO_ESQ">Mão esquerda</option>
786|                        <option value="MAO_DIR">Mão direita</option>
787|                        <option value="MAO">Mão (ambas — legado)</option>
788|                        <option value="TORAX">Tórax</option>
789|                        <option value="ABDOMEN">Abdômen</option>
790|                        <option value="COSTAS">Costas</option>
791|                        <option value="QUADRIL_ESQ">Quadril esquerdo</option>
792|                        <option value="QUADRIL_DIR">Quadril direito</option>
793|                        <option value="QUADRIL">Quadril (ambos — legado)</option>
794|                        <option value="COXA_ESQ">Coxa esquerda</option>
795|                        <option value="COXA_DIR">Coxa direita</option>
796|                        <option value="COXA">Coxa (ambas — legado)</option>
797|                        <option value="JOELHO_ESQ">Joelho esquerdo</option>
798|                        <option value="JOELHO_DIR">Joelho direito</option>
799|                        <option value="JOELHO">Joelho (ambos — legado)</option>
800|                        <option value="PERNA_ESQ">Perna esquerda</option>
801|                        <option value="PERNA_DIR">Perna direita</option>
802|                        <option value="PERNA">Perna (ambas — legado)</option>
803|                        <option value="PE_ESQ">Pé esquerdo</option>
804|                        <option value="PE_DIR">Pé direito</option>
805|                        <option value="PE">Pé (ambos — legado)</option>
806|                        <option value="MULTIPLO">Múltiplo</option>
807|                    </select>
808|                    <p class="mb-1 small text-muted d-none" id="ev-body-region-tags-label">Regiões selecionadas</p>
809|                    <div id="ev_body_region_tags" class="d-none" role="list" aria-labelledby="ev-body-region-tags-label"></div>
810|                </div>
811|                <div class="form-group mb-0 mt-3 d-none">
812|                    <label for="ev_body_location_detail">Detalhamento da localização <span class="text-muted font-weight-normal">(opcional)</span></label>
813|                    <textarea class="form-control" id="ev_body_location_detail" name="ev_body_location_detail" rows="2" maxlength="2000" placeholder="Ex.: face lateral do joelho, próximo ao cotovelo…"></textarea>
814|                </div>
815|            </div>
816|            </div>{# /#ev-body-map-park #}
817|
818|            {# Caracterizado: valor sincronizado a partir da caixinha do acidentado. Suspeita fica na 1ª etapa. #}
819|            <input type="hidden" id="ev_descaracterizado" name="ev_descaracterizado" value="">
820|            </div>{# /#ev-ap-pessoa-caixinha #}
821|            {# Regra de produto: custo removido de AP — só AM possui custo.
822|               ROS e Quase acidente são comunicativos — sem custo; Ambiental não usa este campo. #}
823|            <p class="small text-muted mb-0">Após a análise, você ainda pode adicionar novas evidências.</p>
824|        </div>
825|
826|        {# ── ACIDENTE MATERIAL ────────────────── #}
827|        <div id="ev-block-am" class="ev-type-block d-none">
828|            <div class="form-row">
829|                <div class="col-6">
830|                    <div class="form-group">
831|                        <label for="ev_asset_type">Tipo do ativo afetado <span class="text-danger">*</span></label>
832|                        <select class="form-control" id="ev_asset_type" name="ev_asset_type">
833|                            <option value="" disabled selected>–</option>
834|                            <option value="MAQUINA">Máquina</option>
835|                            <option value="VEICULO">Veículo</option>
836|                            <option value="ESTRUTURA">Estrutura</option>
837|                            <option value="INSTALACAO_ELETRICA">Instalação elétrica</option>
838|                            <option value="TUBULACAO">Tubulação</option>
839|                            <option value="OUTRO">Outro</option>
840|                        </select>
841|                    </div>
842|                </div>
843|                {# Custo do acidente — só Acidente Material (removido de Acidente Pessoal). #}
844|                <div class="col-6">
845|                    <div class="form-group">
846|                        <label for="ev_estimated_loss">Custo do acidente</label>
847|                        <div class="input-group">
848|                            <div class="input-group-prepend">
849|                                <span class="input-group-text">R$</span>
850|                            </div>
851|                            <input type="number" class="form-control" id="ev_estimated_loss" name="ev_estimated_loss"
852|                                   min="0" step="0.01" placeholder="0,00">
853|                        </div>
854|                    </div>
855|                </div>
856|            </div>
857|            <div class="form-row">
858|                <div class="col-6">
859|                    <div class="form-group">
860|                        <label for="ev_downtime">Parada (horas)</label>
861|                        <input type="number" class="form-control" id="ev_downtime" name="ev_downtime"
862|                               min="0" step="0.5" placeholder="0">
863|                    </div>
864|                </div>
865|            </div>
866|            <div class="form-group">
867|                <div class="form-check">
868|                    <input class="form-check-input" type="checkbox" id="ev_operational_impact" name="ev_operational_impact" value="1">
869|                    <label class="form-check-label" for="ev_operational_impact">Impacto operacional?</label>
870|                </div>
871|            </div>
872|            {# Brenda: só Tipo de barreira (sem Dimensão / Barreira que falhou). #}
873|            {% include 'ssma/occurrence/partials/_ev_shared_barrier.html.twig' with {
874|                _barrier_suffix: '_am',
875|                _hide_failed_barrier: true,
876|                _show_barrier_help: true
877|            } %}
878|        </div>
879|
880|        {# ── ACIDENTE AMBIENTAL ───────────────── #}
881|        <div id="ev-block-aa" class="ev-type-block d-none">
882|            {# Meio/poluente/volume/contenção → Identificação (ev-aa-ident-fields). Impacto externo removido. #}
883|            <div class="form-group">
884|                <label for="ev_affected_area">Área afetada</label>
885|                <input type="text" class="form-control" id="ev_affected_area" name="ev_affected_area"
886|                       placeholder="Descreva a área afetada (opcional)">
887|            </div>
888|            {# Brenda: só Tipo de barreira (sem Dimensão / Barreira que falhou). #}
889|            {% include 'ssma/occurrence/partials/_ev_shared_barrier.html.twig' with {
890|                _barrier_suffix: '_aa',
891|                _hide_failed_barrier: true,
892|                _show_barrier_help: true
893|            } %}
894|        </div>
895|
896|        </div>{# /ev-technical-section #}
897|
898|        {# Ação imediata — Aprofundamento (lista repetível) #}
899|        <div class="card app-card-surface p-3 mb-0" id="ev-corrective-actions-section">
900|            <div class="d-flex align-items-center justify-content-between mb-2">
Request #9 deepseek-v4-flash P:28.7K C:571 CR:23.3K CW:0 4473ms
Reasoning
The card markup is in `_ev_injured_person_box.html.twig` which is a separate file (not in the review group though, but the rule.json was modified to add a rule for it). Let me examine where `.ev-inj-descaracter` markup lives and whether the checkbox `.ev-inj-suspect-chk` is in that box. Wait — the review_files contains only the three files: `.opencodereview/rule.json`, `templates/ssma/occurrence/partials/_modal_event.html.twig`, and per other_changed_files... Actually the review_files includes only two files. But there is also the `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` mentioned as a changed file in sample (md=1). It isn't listed in the <review_files>, so per instructions I focus only on review_files. Actually the review_files has 2 entries: `.opencodereview/rule.json` and `_modal_event.html.twig`. Wait the header says "Other files changed in this update (not in this review group): <other_changed_files>" but empty. And review_files contains two files. So the rule .md file might be in other_changed_files? Not listed. Let me check it anyway to understand the rule reference, but we cannot comment on it. Let me read the file `_ev_injured_person_box.html.twig` since it's the markup of the card referenced, and check whether the new `.ev-inj-descaracter` markup includes the suspect checkbox for creation mode. It might help verify the fix actually works as expected, and there could be an issue where the checkbox is inside `.ev-inj-descaracter` and hidden until the type is selected. Let me read that partial file for the descaracter section.
Tool Calls (2)
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig"], "search_text": "ev-inj-descaracter|descaracter-suspect|ev-inj-suspect", "use_perl_regexp": true}
File: templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig
Match lines: 10
192|        <div class="ev-inj-descaracter mt-3 pt-3 border-top d-none">
194|                <input class="form-check-input ev-inj-suspect-chk" type="checkbox">
197|            <div class="ev-inj-descaracter-yesno-wrap d-none">
200|                    <i class="fas fa-info-circle text-muted ml-1 ev-inj-descaracter-tip"
206|                <div class="ev-inj-descaracter-toggle d-flex align-items-center mb-3" role="group" aria-label="Caracterizado como acidente">
207|                    <button type="button" class="mhs-btn-cancel ev-inj-descaracter-btn" data-descaracter-val="0" style="min-width:84px;">Sim</button>
208|                    <button type="button" class="mhs-btn-cancel ev-inj-descaracter-btn" data-descaracter-val="1" style="min-width:84px;">Não</button>
210|                <div class="form-group mb-0 ev-inj-descaracter-comment-wrap">
212|                    <textarea class="form-control form-control-sm ev-inj-descaracter-comment"
216|                <p class="ev-inj-descaracter-no-perm small text-muted mb-0 mt-2 d-none">

file_read
Show Details
{"file_path": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md"}
File: .opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md (Total lines: 63)
IS_TRUNCATED: false
LINE_RANGE: 1-63
1|#### Escopo desta regra
2|
3|Checkbox **“Há suspeita de descaracterização do acidente?”** no card do acidentado durante **Novo evento** (Acidente com Pessoa). Complementa a PR #636 (aprofundamento) e a #677 (exibir bloco na criação).
4|
5|Arquivos cobertos:
6|- `templates/ssma/occurrence/partials/_modal_event.html.twig` — `evSyncDescaracterStageUi`, `evCreateInjuredPersonCard`, `evRenderInjuredPersonBoxes`, CSS `.ev-inj-descaracter*`
7|- `templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig` — bloco `.ev-inj-descaracter` / `.ev-inj-suspect-chk`
8|
9|Fora de escopo: validar ocorrência / SMTP (#671), painel do Plano de Ação.
10|
11|---
12|
13|#### Problema de negócio
14|
15|Felipe/produto (Figma Etapa 8, `docs/ssma/ocorrencia-06-tudo-na-caixinha-descaracter.md`): o checkbox deve aparecer **no card do acidentado na criação**, não só no Aprofundamento técnico.
16|
17|A #677 liberou o bloco na criação, mas o fluxo tipo → pessoa → card recriava o card com `d-none` sem novo sync — checkbox invisível. Esta PR chama `evSyncDescaracterStageUi()` após montar/atualizar cards.
18|
19|---
20|
21|#### Comportamento esperado — bloqueante se quebrar
22|
23|**Novo evento (criação), tipo `ACIDENTE_PESSOAL`:**
24|- Ao adicionar pessoa e expandir o card, o checkbox de suspeita aparece abaixo de *“Oriente o profissional…”*.
25|- **Só** o checkbox fica visível; Sim/Não, comentário e “Sem permissão” permanecem ocultos (CSS `#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle` etc.).
26|- Marcar o checkbox atualiza `data-descaracter-suspect` no card e espelha em `#ev_descaracter_suspect` para o payload (`descaracter_suspect: 1`).
27|
28|**Aprofundamento técnico (`evAprofundamentoOnlyMode`):**
29|- Comportamento da #636/#677 inalterado: Sim/Não quando suspeita marcada; gate de permissão `SSMA_CAN_DESCARACTERIZE` / `evCanDescharacterizeAccident()`.
30|
31|**Outros tipos (ROS, Ambiental, Quase Acidente…):**
32|- `.ev-inj-descaracter` continua oculto (`!isAp` em `evSyncDescaracterStageUi`).
33|
34|**Checkbox global `#ev-suspeita-wrap` (etapa 1):**
35|- Permanece oculto — suspeita é por card, não no registro geral.
36|
37|---
38|
39|#### Sync obrigatório após montar card
40|
41|`evSyncDescaracterStageUi()` deve ser chamado ao final de:
42|- `evCreateInjuredPersonCard()`
43|- `evRenderInjuredPersonBoxes()`
44|
45|Remover essas chamadas faz o checkbox sumir no fluxo normal (tipo → pessoa → card). **Não** tratar como “código desnecessário”.
46|
47|---
48|
49|#### O que NÃO sinalizar como bloqueio nesta PR
50|
51|1. **Checkbox na criação vs só aprofundamento** — intencional por produto; não pedir para esconder de novo na criação.
52|2. **Sim/Não ausente na criação** — intencional; só no aprofundamento.
53|3. **Duplicar lógica da #636** — esta PR só garante visibilidade do checkbox após render do card.
54|
55|---
56|
57|#### Como testar
58|
59|1. Novo evento → Acidente com Pessoa → adicionar pessoa → expandir card → checkbox visível.
60|2. Marcar suspeita → Sim/Não **não** aparece na criação.
61|3. Salvar → Aprofundamento técnico → Sim/Não funciona com suspeita marcada.
62|4. Tipo ROS → bloco não aparece.
63|
Request #10 deepseek-v4-flash P:30.7K C:153 CR:29.18K CW:0 1878ms
Reasoning
Now let me look at the markup of `.ev-inj-descaracter` (line 192) in `_ev_injured_person_box.html.twig`, to understand whether the checkbox is always present in the card clone and that the fix works.
Tool Calls (1)
file_read
Show Details
{"end_line": 225, "file_path": "templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig", "start_line": 180}
File: templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig (Total lines: 225)
IS_TRUNCATED: false
LINE_RANGE: 180-225
180|            </div>
181|
182|            {# CAT eSocial — exibido quando integração estiver ativa #}
183|            <div class="ev-inj-esocial-cat-wrap d-none mt-2 p-2 rounded border">
184|                <div class="small font-weight-bold mb-1">Registro da CAT no eSocial (automático)</div>
185|                <div class="ev-inj-esocial-cat-body small text-muted">—</div>
186|            </div>
187|        </div>
188|
189|        <p class="small text-muted font-italic mb-0 mt-2">Oriente o profissional a anexar evidências (fotos, laudos) na etapa de registro, se aplicável.</p>
190|
191|        {# Descaracterização — visível na criação e no aprofundamento para Acidente Pessoal. Na criação: só checkbox de suspeita; Sim/Não e comentário ficam ocultos via CSS até o aprofundamento. #}
192|        <div class="ev-inj-descaracter mt-3 pt-3 border-top d-none">
193|            <div class="form-check mb-2">
194|                <input class="form-check-input ev-inj-suspect-chk" type="checkbox">
195|                <label class="form-check-label font-weight-bold">Há suspeita de descaracterização do acidente?</label>
196|            </div>
197|            <div class="ev-inj-descaracter-yesno-wrap d-none">
198|                <label class="d-block small mb-2 font-weight-bold">
199|                    Caracterizado como acidente?
200|                    <i class="fas fa-info-circle text-muted ml-1 ev-inj-descaracter-tip"
201|                       style="cursor:help;font-size:12px;"
202|                       data-toggle="tooltip"
203|                       data-placement="top"
204|                       title="Sim = é acidente. Não (descaracterizar) só quem tem permissão na coluna Descaracterizar em Profissionais de Saúde."></i>
205|                </label>
206|                <div class="ev-inj-descaracter-toggle d-flex align-items-center mb-3" role="group" aria-label="Caracterizado como acidente">
207|                    <button type="button" class="mhs-btn-cancel ev-inj-descaracter-btn" data-descaracter-val="0" style="min-width:84px;">Sim</button>
208|                    <button type="button" class="mhs-btn-cancel ev-inj-descaracter-btn" data-descaracter-val="1" style="min-width:84px;">Não</button>
209|                </div>
210|                <div class="form-group mb-0 ev-inj-descaracter-comment-wrap">
211|                    <label class="small mb-1">Comentário <span class="text-danger">*</span></label>
212|                    <textarea class="form-control form-control-sm ev-inj-descaracter-comment"
213|                              rows="2"
214|                              maxlength="2000"></textarea>
215|                </div>
216|                <p class="ev-inj-descaracter-no-perm small text-muted mb-0 mt-2 d-none">
217|                    Sem permissão para descaracterizar o acidente.
218|                    Peça ao gestor que marque a coluna <strong>Descaracterizar</strong> em
219|                    Profissionais de Saúde (configuração de aprofundamento).
220|                </p>
221|            </div>
222|        </div>
223|    </div>
224|</div>
225|
Request #11 deepseek-v4-flash P:31.73K C:1.65K CR:30.85K CW:0 12220ms
Reasoning
Now I understand fully. The card clone always contains the `.ev-inj-descaracter` div with `d-none` default. The fix calls `evSyncDescaracterStageUi()` which toggles `d-none` based on `isAp` and toggles yes/no wrap based on checkbox state. Now evaluate potential issue: In creation mode, when the card is collapsed (default), the descaracter section might be inside a collapsed area? Not relevant to visibility at creation. Let me think about whether the added call at line 2709 in `evCreateInjuredPersonCard` will run in cases where it could cause unintended effect (e.g., during modal pre-population when editing existing event in general step with a type that is ACIDENTE_PESSOAL but not yet selected). Actually, editing an event that is AP in mode create/edit: cards pre-rendered via `evRenderInjuredPersonBoxes`, then type is selected afterwards? If type was already selected in the select when the render happens, `isAp` is true and the block shows. If type not yet chosen (unlikely), block stays hidden and will sync when the user chooses type at line 5288. Also there is another scenario: ROS/Quase acidente types where people involved exist but cards are hidden — `evSyncInjuredCardsFromInvolved` at 2347: if type not AP, it returns early. So cards only exist for AP. OK. Now consider a possible real issue regarding calling `evSyncDescaracterStageUi` inside `evCreateInjuredPersonCard` when creating the very first card in aprofundamento read-only mode (validation flow, reopen): it toggles the yesNoWrap based on the checkbox `.ev-inj-suspect-chk`. When the card is populated via `evApplyCardInjuryData`, does it set the checkbox state? Presumably yes (data-descaracter-suspect / suspect data). Let me check `evApplyCardInjuryData` to see if it sets `.ev-inj-suspect-chk` checked. If it does and the yes/no wrap should then show based on state, the call at the end of `evCreateInjuredPersonCard` is after `evApplyCardInjuryData` and after `evBindInjuredCardEvents`. So fine. Let me verify whether when creating a card, before fix, in aprofundamento, previously the yes/no wrap may be displayed only if the section was synced from elsewhere (type change or step change to aprofundamento triggers evSyncDescaracterStageUi at line 3398 in evSetStep). But when in the same aprofundamento step and user adds a new injured person through an async validation? Actually can you add a person while in aprofundamento? Perhaps via editing saved draft reopening. Anyway, not a bug. Actually, wait—could there be a case where the new call at line 2709 runs BEFORE the checkbox is set for an existing card? In `evCreateInjuredPersonCard`, when `existing` is found, it returns before applying saved data? Actually at line 2658: `existing` returns early without re-applying data or sync. That is unchanged behavior. New card path runs sync after apply. Now think about whether there's any issue with `evSyncDescaracterStageUi` being called while the modal is still being built, e.g., if `evCreateInjuredPersonCard` is invoked by `evRenderInjuredPersonBoxes` when other parts of the form (like the type select) don't exist yet — but query selectors are safe (guarded). Let me also consider the sync at 2826 in `evRenderInjuredPersonBoxes`. Since cards are created by loop that already syncs, it's redundant but harmless. Not worth flagging heavily; maybe mention as minor that the evCreateInjuredPersonCard call makes 2826 redundant—but wait: `evRenderInjuredPersonBoxes` may render 0 cards (no details). If no card is created, but a leftover `.ev-inj-descaracter` section visible from a previous render? `wrap.innerHTML=''` clears it. So sync not needed then either. It's purely defensive/redundant. Not an issue per se. It is idempotent, cost negligible. I might not comment. Now the JSON file check: Keys `path`, `merge_system_rule`, `rule` correct spelling. Also note the JSON ordering places the two new entries right before `src/Controller/SsmaController.php`. All consistent. No issue. The rules for .opencodereview/rule.json: "Check JSON files for spelling errors in json-keys; ignore the content of json-values." So no issue. Now per user-specific rules for the twig file, we must consider: 1. God template — 7712 lines template mixing markup and scripts. PR adds only 2 calls. The rule states if template is god template, any PR increasing the mix should be flagged before any other finding. Here PR doesn't add new markup/script; it only calls an existing function at 2 places. Still, the sync function call is added to existing giant script logic in the template, which is part of the maintenance concern, but the addition itself is trivial. I'd flag as low-severity maintainability note pointing to the established guidance to move screen logic to public/js/ — but honestly, since there is a dedicated rule file the repo now maps, and the codebase pattern seems to be template-script heavy, I might give a low-severity attention note. Actually wait: the .opencodereview/rules file says "Não sinalizar como bloqueio... Duplicar lógica da #636" etc. And there is a repo-specific rule added mapping `_modal_event.html.twig` to `occurrence-descaracter-suspeita-card.md`. That rule file itself says the sync must be called in these two functions. Since the change is aligned with that doc, it's intended. But note: review files include `.opencodereview/rule.json` and the template; the mapping to the rule md means system will use these custom rules for the review. The user rules in the system prompt have strong requirements. I should be careful with my comments. Let me identify real potential problems: Issue 1 — A true potential regression: The added `evSyncDescaracterStageUi()` in `evCreateInjuredPersonCard` is called even when the card is created in the **aprofundamento** step while the card belongs to a person whose checkbox `ev-inj-suspect-chk` was not yet populated. Let me check whether `evApplyCardInjuryData` sets the checkbox. Let's search for `.ev-inj-suspect-chk` assignment / `data-descaracter-suspect`. Line 6484 and 5749 and 2934. Let me read line 6460-6500.
Tool Calls (2)
file_read
Show Details
{"end_line": 6500, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 6460}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 6460-6500
6460|            // Re-sync depois das tags + details: garante caixinhas médicas mesmo se
6461|            // setTagSelectValues rodou antes do hidden ou membro não estava no select.
6462|            syncBodyRegionVisibility();
6463|            evSyncInjuryFieldsByConsequence();
6464|            if (typeof window.evSyncLtiAvailability === 'function') { window.evSyncLtiAvailability(); }
6465|            evSyncInjuredCardsFromInvolved();
6466|            // Se sync ainda não viu pessoas nas tags, remonta a partir dos details salvos.
6467|            var wrapAfter = document.getElementById('ev_injured_person_boxes');
6468|            var hasMedCards = !!(wrapAfter && wrapAfter.querySelector('.ev-injured-person-box[data-person-id]'));
6469|            if (!hasMedCards && Object.keys(evGetInjuredDetailsObj() || {}).length) {
6470|                evRenderInjuredPersonBoxes();
6471|            }
6472|            if (evCurrentStep === 'aprofundamento' && typeof evEnsurePrimaryInjuredCardExpanded === 'function') {
6473|                evEnsurePrimaryInjuredCardExpanded();
6474|            }
6475|        }
6476|
6477|        // ── Descaracterização ────────────────────────────────
6478|        // Restaura suspeita por card a partir do det ou do injured_person_details do card primário.
6479|        var suspectRaw = det.descaracter_suspect != null ? det.descaracter_suspect : data.descaracter_suspect;
6480|        var suspectOn = suspectRaw === true || suspectRaw === 1 || suspectRaw === '1';
6481|        evSetChk('ev_descaracter_suspect', suspectOn);
6482|        // Propaga suspeita para cada card (checkbox editável no aprofundamento).
6483|        document.querySelectorAll('.ev-injured-person-box').forEach(function (card) {
6484|            var chk = card.querySelector('.ev-inj-suspect-chk');
6485|            if (chk) chk.checked = suspectOn;
6486|            card.setAttribute('data-descaracter-suspect', suspectOn ? '1' : '0');
6487|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
6488|            if (yesNoWrap) yesNoWrap.classList.toggle('d-none', !suspectOn);
6489|        });
6490|        var descVal = det.descaracterizado != null ? det.descaracterizado : data.descaracterizado;
6491|        if (descVal === true || descVal === 1) descVal = '1';
6492|        if (descVal === false || descVal === 0) descVal = '0';
6493|        evSetVal('ev_descaracterizado', descVal == null ? '' : String(descVal));
6494|        evSyncDescaracterUi();
6495|
6496|        // ── Evidências já anexadas ──────────────────────────
6497|        var evidences = Array.isArray(det.evidences) ? det.evidences : (Array.isArray(data.evidences) ? data.evidences : []);
6498|        evEvidences = evidences.map(function (e) {
6499|            return {
6500|                name: e.name || e.filename || '',
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "data-descaracter-suspect"}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 3
5748|            // Atualiza o data-descaracter-suspect do card para serialização.
5749|            card.setAttribute('data-descaracter-suspect', e.target.checked ? '1' : '0');
6486|            card.setAttribute('data-descaracter-suspect', suspectOn ? '1' : '0');

Request #12 deepseek-v4-flash P:34.33K C:1.31K CR:33.28K CW:0 10300ms
Reasoning
Now I understand the whole picture. The fix is straightforward and low-risk. Let me now determine the actual code review findings I'll provide. Given the code: 1. In `evRenderInjuredPersonBoxes`, the added line 2826 `evSyncDescaracterStageUi()` is redundant because each `evCreateInjuredPersonCard` call (line 2813) already syncs (new line 2709). However, redundancy is harmless and defensive. Not a bug. Could note but the doc says "Remover essas chamadas faz o checkbox sumir" — the repo-specific rule explicitly demands both. Given the added rule file says sync must be called in both functions, commenting that 2826 is redundant contradicts the repo guidance that says don't remove. Given the rule md explicitly asks both, I shouldn't comment on redundancy. 2. God template concern: The template is 7712 lines with a giant script. This PR adds calls to an existing function that itself is inside the same giant `<script>`. Should we raise the "god template" flag? The rule says any PR that increases this mixing should be flagged first. But this change only adds two calls to existing functions — it doesn't introduce new mixing. It's not increasing the mix. Given they're consistent with the existing structure and this is a fix PR of 2 lines, flagging the god template might be noise, but the user-specific rule mandates flagging "qualquer PR que aumente ainda mais essa mistura". Adding calls inside a big script in a template does technically continue the pattern but adds essentially zero new script logic. I think a low-severity maintainability comment referencing the god-template rule would be reasonable, but I want to be careful not to over-flag. The rules also say "Evite comentários sobre código correto". Hmm. Given the overall instructions: "Be objective and neutral, make judgments based on facts and logic." The repo rule file now (added in this PR) maps the template and the partial to a dedicated OCR rule that explicitly validates this behavior; there's a committed test/manual procedure. Since the diff itself is tiny and correct, we can either give a small maintainability/low-severity note about the god template or just approve. Let me double check for actual bugs around the new calls: Potential subtle bug: In `evCreateInjuredPersonCard`, the new call to `evSyncDescaracterStageUi()` occurs AFTER `evEnsurePrimaryInjuredMarked()`. `evEnsurePrimaryInjuredMarked` may call `evSelectPrimaryInjuredPerson` which might render something. Fine. Potential subtle bug 2: `evSyncDescaracterStageUi()` is invoked at line 2709 for EVERY new card, including when the type is AP but the current step is 'general' (creation), where `.ev-inj-descaracter` gets `d-none` removed → the section becomes visible in the collapsed card. That's exactly intended by the fix (#677 wanted visible in creation when expanded). Potential subtle bug 3: The card may be collapsed by default on creation (`evCollapseInjuredCard(card, true)`), so the sync toggles the section inside a hidden area — visible when expanded. Potential subtle bug 4: When `evCreateInjuredPersonCard` is called within `evRenderInjuredPersonBoxes`, which is called from the modal data loading path (line 6470) inside a data-population function (probably for edit/aprofundamento open). During this load, before the "Descaracterização" restore block at 6478, each card creation triggers `evSyncDescaracterStageUi` which toggles yesNoWrap per card's unchecked checkbox state. But then later the restore block sets checkbox and toggles yesNoWrap. The order: line 6470 calls evRenderInjuredPersonBoxes which internally creates cards and syncs; then 6472 evEnsurePrimaryInjuredCardExpanded → evSyncInjuredCardsFromInvolved may add cards again; Then 6478 restore descaracter state per card; finally 6494 evSyncDescaracterUi → evSyncDescaracterStageUi again. So final state correct. But what about the case where `evRenderInjuredPersonBoxes` is called BEFORE the type select is populated in edit mode (i.e., while evSelectedType() still ''), and then type is set later? Cards would get hidden section; when type set later, evSyncTypeFields triggers sync at 5288 toggling visibility. Fine. Potential subtle bug 5: Performance: `evSyncDescaracterStageUi` queries whole document; called for each card in a loop (N+1 redundancy within `evRenderInjuredPersonBoxes`). But number of cards is small. I conclude no blocking bug. Now, let's revisit the JSON rule file for json key spelling: all keys correct. No issue. Regarding the second file in review_files, only rule.json and the twig are present. Wait—the review group in <review_files> includes only rule.json and _modal_event.html.twig. So we should focus on those two files. Let me re-read the twig diff: two added lines calling `evSyncDescaracterStageUi();`. Both correct with indentation of 8 spaces matching context. Wait, are we sure `evSyncDescaracterStageUi` is defined in scope at those call sites? Both inside the same `<script>` presumably. Let me verify that the function is inside the same `<script>` tag. Function declarations hoist within their `<script>`. Search to see if there are multiple `<script>` blocks in the template and where the function is defined relative to where called. Let me search for `<script>` occurrences in the file and their line numbers.
Tool Calls (1)
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "<script|</script>"}
No matches found
Request #13 deepseek-v4-flash P:35.65K C:294 CR:35.58K CW:0 2737ms
Reasoning
No `<script>` tags? Interesting. The functions may be defined within a `{% block javascripts %}` or `{{ source }}`... Actually search returned no results. Perhaps the file is included via a script from somewhere else; or the script tags contain twig tags like `{% raw %}`? Hmm. Search didn't find literal `<script`. Maybe the JS is in a `<script>` written as `%}` ... Actually file_read earlier shows lines with JS code like functions. The `<script>` might be like `<script type="text/javascript">`? Search may be case-insensitive... default is case-insensitive so `<script` would match. Hmm no match means no script tags in this file? Wait, maybe the file starts with `{% verbatim %}` or JS embedded without script tag? That seems odd. Could the file use Twig `{{ block('...') }}`? Wait, maybe the search tool has trouble with the `<` characters? Perhaps escaping? Let me search for "script" text alone.
Tool Calls (1)
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "script"}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 25
4|{# Escopo do arquivo inteiro (scripts após endembed não veem variáveis definidas só em modal_body). #}
50|        <script>
64|        </script>
1432|<script>
3160|                    '<input type="text" class="form-control ev-ca-description" ' +
3162|                        'value="' + evCorrectiveEscape(data.description || '') + '">' +
3223|            var description = ((item.querySelector('.ev-ca-description') || {}).value || '').trim();
3229|            if (!description && !responsibleId && !validatorId && !hierarchy && !deadline && !resolved) {
3233|                description: description,
3269|            var description = ((item.querySelector('.ev-ca-description') || {}).value || '').trim();
3275|            var empty = !description && !responsibleId && !validatorId && !hierarchy && !deadline && !resolved;
3277|            if (!description) {
3279|                if (MV && window.jQuery) MV.markInvalid(window.jQuery(item.querySelector('.ev-ca-description')));
6183|        evSetVal('ev_title',    det.title    || (data.description || '').split('\n')[0] || '');
6187|        evSetVal('ev_activity', det.activity  || data.activity || data.description || '');
6896|            description:      descCombined,
7273|                'description': 'Registro do ocorrido'
7279|                if (text.indexOf('details.') === -1 && !/^(consequence|type|location|manager_id|description|nature|agent)\s*:/i.test(text)) {
7458|                body: JSON.stringify({ text: text, field: 'activity_description', context: type })
7548|        function maybeSuggestFromDescription() {
7584|            debounceTimer = setTimeout(maybeSuggestFromDescription, 300);
7588|            debounceTimer = setTimeout(maybeSuggestFromDescription, 1800);
7592|</script>
7593|<script>
7711|</script>

Request #14 deepseek-v4-flash P:36.45K C:2.46K CR:35.84K CW:0 19023ms
Reasoning
So there is a `<script>` at line 1432 which contains functions at lines 2653-2947 (evCreateInjuredPersonCard at 2653, evSyncDescaracterStageUi at 2917) — all inside the same script that starts at 1432 and presumably ends at line 7592. Let's confirm: lines 7592 `</script>` after the one starting 1432. And there are 3 scripts: line 50, line 1432, line 7593. So lines 2653, 2709, 2826, 2917 are all within the 1432–7592 script. Same scope, function hoisting applies. Good. So no scope bug. Now verify there is no duplicate call issue causing, e.g., a recursion? evSyncDescaracterStageUi doesn't call evCreateInjuredPersonCard or evRenderInjuredPersonBoxes, no recursion. I'm fairly confident the change is correct and minimal. Now, should I provide any comments at all? Consideration of potential issues worth noting: A) Redundancy / double work: The second call at 2826 duplicates what the loop already does. Since each card creation at line 2813 already triggers the sync, the call at 2826 is redundant when at least one card is rendered (but covers empty case trivially). This isn't a problem. B) God template maintenance note: Low severity; the repo guidance demands the OCR. But given the specific repo-specific rule file added (occurrence-descaracter-suspeita-card.md) explicitly instructs the sync at the end of those two functions, plus the user-specific rule mandates the "God template" flag for PRs that add more mixing. This PR only adds calls, no new markup or new functions. So there's no "new mix". I think a comment is optional. Given "Focus on issues in newly added code" and "avoid commenting on correct code," maybe we don't need to comment. But let me think about whether calling `evSyncDescaracterStageUi()` inside `evCreateInjuredPersonCard` is in the right place semantically: it runs after `evBindInjuredCardEvents(card)` — that binds listeners incl. the suspect checkbox change delegated event at document level? Actually event delegation on document for `.ev-inj-suspect-chk` change. `evBindInjuredCardEvents` binds other events. Order okay. Now consider a scenario where the fix doesn't cover: When creating a card via "existing card" return path (duplicate add) or when only updating an existing card summary (`evUpdateInjuredCardSummary`), no sync. Not required. Actually, wait. Let me reconsider the stated problem: "Ao criar o card, o bloco .ev-inj-descaracter continuava com d-none porque evSyncDescaracterStageUi() só rodava ao mudar o tipo, não depois de evCreateInjuredPersonCard / evRenderInjuredPersonBoxes." The fix addresses both functions. Good. Now potential issue: There's another route where cards are created: `evSyncInjuredCardsFromInvolved` at line 2378 calls `evCreateInjuredPersonCard`, so sync now runs there too. Good — covers the tags-based "Pessoas envolvidas" flow (which is the primary flow per comment at 2747). Let me examine whether evSyncDescaracterStageUi hides `#ev-suspeita-wrap` always and whether this sync in creation context might remove `.ev-inj-descaracter`'s d-none when the modal is for other types at the moment of adding a card in non-AP. In non-AP type, people field hidden, cards never created. Fine. Consider test for other types: type ROS, adding a person? People field hidden for ROS (`evSyncPeopleFieldForType` hides and clears for ROS). Cards only created for AP. Another subtle risk: This template file is included (maybe for edit event modal) where `evSelectedType()` returns something else while a draft restores an AP event, and the sync is triggered during the initial render before the type is set — but there's a final sync at 3398/5288 when type changes. In any case it will be consistent. There's also a concern about `evAprofundamentoOnlyMode` variable - used in `evSyncDescaracterStageUi` at 2919 `var isDoctorAprof = !!evAprofundamentoOnlyMode;`. If that variable is declared later inside the script (e.g., `var evAprofundamentoOnlyMode = ...`), accessing before assignment gives `undefined` but hoisted var → undefined → falsy. Let me check where evAprofundamentoOnlyMode is declared. Not needed for this diff. Given all, I think the change is correct and we might not have blocking issues. But per the user-specific rules, I should provide review comments for genuine issues. Let me examine two potential issues more deeply: 1. When `evCreateInjuredPersonCard` is invoked for an existing card early return path... Actually wait, there's a subtle new behavior: line 2658-2659 `existing` returns WITHOUT `evExpandInjuredCard`, etc. Actually it returns before, unchanged. 2. Hmm. Let me consider `evRenderInjuredPersonBoxes` being called in aprofundamento context (edit). Suppose the current user does NOT have permission to descaracterizar (`SSMA_CAN_DESCARACTERIZE` false). In `evSyncDescaracterUi` there's logic that pre-selects "Sim" (`hidden.value='0'`) when !canSn and empty and aprofundamento. That's a different function that calls `evSyncDescaracterStageUi`. Not new. 3. New calls happen even when the checkbox flow isn't intended (e.g., in 'aprofundamento' read-only mode where cards are disabled). `evSyncDescaracterStageUi` only toggles classes, does not enable/disable. `evSetAprofundamentoReadonly` at 3300-3323 does disable and then re-sync. Fine. So no bug in the new code. Now, for the JSON file — adding two entries. But wait: the added entries map `templates/ssma/occurrence/partials/_modal_event.html.twig` and `_ev_injured_person_box.html.twig` to the same rule file that itself describes this PR's intended behavior (the .md file added in the same commit? sample says md=1 changed, so yes). This means future reviews of these templates will apply this rule and consider missing sync calls as issues. That is fine and consistent. Check JSON file ordering - the rules array ordering isn't semantically important. Keys spelled fine. Is the rule.json path correct — the file `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` exists (we read it, 63 lines). Good. So overall, no blocking issues. Perhaps I should give one low/medium comment about the redundancy in `evRenderInjuredPersonBoxes` (the sync call is duplicated because evCreateInjuredPersonCard already performs it inside the loop; each card triggers an extra full-document sync). But the repo-specific doc explicitly requires the call at end of both functions and says do not treat as unnecessary. So no comment. I could also consider a small comment about the god template pattern (maintainability low severity): the JS continues to live in a giant script inside the template. The user-specific rule says to flag. But since this is a tiny fix in existing code, flagging the god template may be considered low value; however the instruction says "God template — maior peso. Se este template já é um 'god template' ..., qualquer PR que aumente ainda mais essa mistura deve ser sinalizada antes de qualquer outro achado." This PR does not increase the mixing much—only two calls. Hmm, "qualquer PR que aumente ainda mais essa mistura" — technically adding lines to the script is "increase". I think we can mention it as a low-priority maintainability note: "A correção está correta, mas reforça o padrão de lógica de tela dentro do template (script de ~6000 linhas); para mudanças futuras, considerar mover evSyncDescaracterStageUi e os handlers para public/js/ssma/..." — It fits the requested format: direct explanation first, impact, suggestion. Severity low. Given this is a 2-line fix and the repo added a rule file that explicitly maps this template for OCR, plus the project's own checklist acknowledges the template as containing these functions, I think one low-severity maintainability comment is warranted, plus no blocking issues. Let me consider whether to comment on anything else. Let's double-check the rule.json again: Added entries appear AFTER `templates/ssma/action_plan/tabs/_tab_painel*.html.twig` mapping and before `src/Controller/SsmaController.php`. JSON structure correct. Trailing commas fine. Alright, let me also consider test coverage per checklist: This is a JS UI change with no test files (not a JS file under public/js). The rule file mentions manual testing. Given there's no automated JS test infra for template scripts, we probably won't flag missing tests strongly. Given repository may not have a JS unit test harness for Twig-embedded scripts; but rules mention "Test Coverage" — could mention absence of automated coverage, but given repo pattern (rule file), no. Let me also double check there isn't a subtle issue where, during `evCreateInjuredPersonCard` inside `evRenderInjuredPersonBoxes`, the card is still in `wrap` with previous innerHTML cleared; then `evSelectPrimaryInjuredPerson(preferred)` occurs after, and that may re-render summary or expand. The sync at 2826 after select also is fine. Hmm, wait—one more nuance: The added sync in evCreateInjuredPersonCard happens BEFORE `evRenderInjuredPersonBoxes` decides the primary and expands. In creation mode, cards start collapsed; the section hidden within collapsed card is shown (class d-none removed) but collapsed. When expanded later by the user, block visible. Great. What about the case at line 2694-2698 (aprofundamento, expand if first card)? The section visible. Fine. Now there is one possible actual defect worth checking: In `evCreateInjuredPersonCard`, when `expand` is falsy and step is general, the card gets `evCollapseInjuredCard(card, true)`. Could collapsing remove the `.ev-inj-descaracter` markup or reset classes? Let me quickly look at evCollapseInjuredCard and evExpandInjuredCard to ensure they don't toggle `.ev-inj-descaracter` d-none via innerHTML re-render that would wipe the sync. Search.
Tool Calls (1)
code_search
Show Details
{"file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"], "search_text": "function evCollapseInjuredCard|function evExpandInjuredCard", "use_perl_regexp": true}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 2
2421|    function evCollapseInjuredCard(card, skipSync) {
2448|    function evExpandInjuredCard(card) {

Request #15 deepseek-v4-flash P:38.98K C:97 CR:38.78K CW:0 1550ms
Tool Calls (1)
file_read
Show Details
{"end_line": 2475, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2400}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2400-2475
2400|        if (!card) return null;
2401|        if (!card.classList.contains('is-expanded')) {
2402|            evExpandInjuredCard(card);
2403|        } else if (typeof syncInjuryFieldsForCard === 'function') {
2404|            syncInjuryFieldsForCard(card);
2405|        }
2406|        if (typeof evFilterCardConsequences === 'function') {
2407|            evFilterCardConsequences(card);
2408|        }
2409|        document.querySelectorAll('#ev_injured_person_boxes .ev-injured-person-box[data-person-id]').forEach(function (c) {
2410|            if (typeof syncInjuryFieldsForCard === 'function') {
2411|                syncInjuryFieldsForCard(c);
2412|            }
2413|            if (typeof evFilterCardConsequences === 'function') {
2414|                evFilterCardConsequences(c);
2415|            }
2416|        });
2417|        evRenderInjuredPersonSummary(card, card.getAttribute('data-person-id'));
2418|        return card;
2419|    }
2420|
2421|    function evCollapseInjuredCard(card, skipSync) {
2422|        if (!card || !card.parentNode) return;
2423|        if (card.classList.contains('is-expanded') && !skipSync) {
2424|            // Persiste só este card (não reescreve o mapa inteiro de details).
2425|            var pid = card.getAttribute('data-person-id');
2426|            if (pid) {
2427|                var all = evGetInjuredDetailsObj();
2428|                all[pid] = evReadCardInjuryData(card);
2429|                evSetInjuredDetailsObj(all);
2430|                evPersistDraftSoon();
2431|            }
2432|        }
2433|        card.classList.remove('is-expanded');
2434|        var summary = card.querySelector('.js-ev-inj-summary');
2435|        var form = card.querySelector('.js-ev-inj-form');
2436|        if (summary) summary.classList.remove('d-none');
2437|        if (form) form.classList.add('d-none');
2438|        card.querySelectorAll('.js-ev-inj-toggle.ssma-config-tipo-action-btn').forEach(function (btn) {
2439|            btn.setAttribute('aria-expanded', 'false');
2440|        });
2441|        evUpdateInjuredCardSummary(card);
2442|        if (card.querySelector('.ev-inj-body-map-slot #ev-body-map-block')) {
2443|            evParkBodyMapBlock();
2444|        }
2445|        evClearCanonicalInjuryIds();
2446|    }
2447|
2448|    function evExpandInjuredCard(card) {
2449|        if (!card || !card.parentNode) return;
2450|        var wrap = document.getElementById('ev_injured_person_boxes');
2451|        if (wrap) {
2452|            // 1) Colapsa os outros primeiro (viram resumo curto e continuam no DOM).
2453|            wrap.querySelectorAll('.ev-injured-person-box.is-expanded').forEach(function (other) {
2454|                if (other !== card) evCollapseInjuredCard(other);
2455|            });
2456|        }
2457|        card.classList.add('is-expanded');
2458|        var summary = card.querySelector('.js-ev-inj-summary');
2459|        var form = card.querySelector('.js-ev-inj-form');
2460|        if (summary) summary.classList.add('d-none');
2461|        if (form) form.classList.remove('d-none');
2462|        card.querySelectorAll('.js-ev-inj-toggle.ssma-config-tipo-action-btn').forEach(function (btn) {
2463|            btn.setAttribute('aria-expanded', 'true');
2464|        });
2465|
2466|        // 2) Resumos ficam no topo; o expandido vai para o final — evita “sumir” sob o mapa.
2467|        if (wrap && card.parentNode === wrap) {
2468|            wrap.appendChild(card);
2469|        }
2470|
2471|        evAssignCanonicalInjuryIds(card);
2472|        evRenderInjuredPersonSummary(card, card.getAttribute('data-person-id'));
2473|        var saved = evGetInjuredDetailsObj()[card.getAttribute('data-person-id')] || {};
2474|        if (typeof syncInjuryFieldsForCard === 'function') {
2475|            syncInjuryFieldsForCard(card);
Request #16 deepseek-v4-flash P:40.18K C:4.19K CR:39.04K CW:0 32651ms
Reasoning
The collapse/expand functions don't re-render innerHTML, so the class toggles from sync survive. Let me look at `evRenderInjuredPersonSummary` — summary re-render doesn't touch descaracter block. OK. Given all that, the change is sound. So the code review output: no critical or high issues. Possibly: - Low severity maintainability note about god template / JS inside template (per user-specific rules priority 1). But this change is just two function calls, and the repo's own added OCR rule file documents that the sync must be present. I'll consider including a low-severity note or skip. - A possible low note about the redundancy in `evRenderInjuredPersonBoxes`. But doc says don't treat as unnecessary. So skip. I think maybe I should provide a single comment? Let's weigh. The instructions: "For the current code changes, provide feedback opinions, pointing out areas for improvement or potential issues. Focus on issues in newly added code." If no real issues, don't fabricate. The "God template" priority rule strongly requires flagging whenever PR increases mixing. There's already an enormous script. Two more lines of calls inside the script. This could be a legit low-severity maintainability note: it's aligned with repository guidance that screen logic should be in public/js/. But given the template already has all this logic and this PR is a fix in the same pattern, maybe it's overkill. I'll produce no blocking findings but include one low-severity maintainability observation per user-specific rules (god-template) and possibly mention no tests? Hmm. Let me reconsider - is there truly nothing? Let me re-analyze the interplay of `evSyncDescaracterStageUi` called in `evCreateInjuredPersonCard` during creation when the checkbox might get checked by the user before later re-render? Not relevant. Wait, there's a subtle timing issue: In creation flow adding person via the "Pessoas envolvidas" tags, the card is created via `evSyncInjuredCardsFromInvolved` (line 2378), which is called from tag changes. Then line 2826 is not in that path. The fix's added line at 2709 handles it. Good. Now there is one more thing to verify - the order of call: `evSyncDescaracterStageUi()` uses `evSelectedType()`. If the sync is invoked when the user hasn't selected type yet — e.g., adding an involved person before selecting type is allowed in UI? Let me look at the flow of "Pessoas envolvidas" to see if it's restricted to AP. Actually evSyncPeopleFieldForType hides the people field for ROS and shows for AP/others? Wait: hide only for ROS. So people tags can be added for Quase Acidente/Ambiental too? Actually "involved" people may apply to other types, but injured person cards only for AP. The function `evSyncInjuredCardsFromInvolved` at 2348 exits early if type !== 'ACIDENTE_PESSOAL'. So cards not created for other types. BUT: What if the user first adds involved people while type is something else (e.g., QUASE_ACIDENTE), then changes type to AP? When type changes to AP, `evSyncTypeFields` calls ... and `evSyncInjuredCardsFromInvolved` (probably from evSyncPeopleFieldForType or somewhere) creates the cards. Actually evSyncInjuredCardsFromInvolved is likely called on type change too. In that flow, the type select already has AP selected when sync runs → correct. What if user adds involved people while type = AP but hasn't chosen "Acidente" subtype? Not relevant. Edge: What about creating a card from a person who is NOT in "people involved" but selected via legacy select `ev_person_id` (evAddInjuredPersonFromSelect)? That path creates card too and sync runs. OK. So fix covers all card creation paths. Potential regression risk in the aprofundamento: When editing an existing AP occurrence in aprofundamento and expanding card, sync at 2709 triggers toggling of `.ev-inj-descaracter` visibility only when a new card is created; the existing card path (expanded) doesn't go through evCreateInjuredPersonCard; when page loads, evRenderInjuredPersonBoxes runs with sync. Fine. I'm confident: no functional bug. Now decision on comments. Provide: 1. (Optional low) God template maintainability note — actually because the PR adds lines to the giant embedded script, but there's already a rule doc. I'll consider skipping to avoid noise. But the user-specific rules say the god template is "maior peso... qualquer PR que aumente ainda mais essa mistura deve ser sinalizada antes de qualquer outro achado". Since this is the top priority rule, I think it's safer to include it as an "atenção/leve" note, phrased constructively. But is it a "code issue"? It is maintainability per the generic rules: "Maintainability: Is the code clear... Does it follow project's existing code style and architecture patterns?" The god-template is the project architecture concern. However, the diff is 2 lines and doesn't increase mixing. Hmm. Many reviewers would not flag this. But user-specific instructions explicitly call it out as priority #1. I'll include a short low-severity note. 2. The double call at 2826 redundancy: Even though the doc says keep, a careful reviewer may point out that since evCreateInjuredPersonCard now triggers sync per card, calling again at the end of evRenderInjuredPersonBoxes duplicates work, but harmless; given repo doc explicitly instructs both, mentioning redundancy may be unnecessary. Skip. Actually, wait: there is a subtle consideration: if `evRenderInjuredPersonBoxes` has zero ids, cards are not created and the section might remain visible from a prior state? The prior innerHTML cleared, so no. But if the form previously had a card shown and then `evRenderInjuredPersonBoxes` clears all with no new cards, previously visible `.ev-inj-descaracter` disappears because cards removed; and nothing else has d-none. Fine. Also consider: `evCreateInjuredPersonCard` is now calling a global document query function during heavy re-render loops; performance negligible. 3. Regarding test coverage: This PR contains no automated test for a UI-only JS behavior; existing infra may not support. Given the repo has OCR rules and manual QA steps, skip. I think final output should be minimal. Maybe zero code comments? The task says if a code issue is identified and confirmed, call the tool. If no obvious issue, call task_done. Since I lean toward no functional issues, but there is the mandatory "God template" rule in user-specific instructions that suggests flagging this, I'll include one low severity maintainability comment, and otherwise approve. Hmm, but wait: The rule file added in this PR (`.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md`) even says "**Não** tratar como 'código desnecessário'" about these calls and explicitly says sync calls in both functions are required. So flagging redundancy would contradict the repo rule. Better not. Regarding the god template, let me weigh whether the change "aumenta ainda mais essa mistura". It adds two invocations inside a ~6k line script, but it doesn't introduce new logic/markup in the template. Arguably the change does not "increase the mixing" — it merely hooks into the existing pattern. The god template issue is pre-existing. Commenting on it for a 2-line fix would be low value, but the user-specific rules mention it as top priority with mandatory mention whenever applicable. Given the diff adds code to the giant script (not to public/js/), a reasonable reviewer might note the systemic concern, flagged as low severity, noting this fix is consistent with the existing location, so future PRs should consider migration. I'll add a concise low severity comment. Actually, hmm. Should I make any comments at all? The task states review files individually and cross-file observations encouraged. Files: rule.json (ok, no key spelling issues) and the template. If I make no comments, then call task_done. But per instructions for user-specific rules, the god-template priority stands out; and code style says logic should be in public/js. Let me include a single low-severity note on the template about god template? But I want to avoid comments that look pedantic for a 2-line fix. However, the user-specified priority makes it mandatory-ish: "Se este template já é um 'god template', qualquer PR que aumente ainda mais essa mistura deve ser sinalizada antes de qualquer outro achado." It's better to include it, phrased in pt-BR, direct, no jargon first, structure: what changed & impact; why it matters; what to do. I'll add one low severity maintainability comment on the new line 2709 in the template referencing the fact the fix keeps adding logic to the giant embedded script. Wait — but rule for writing comment: first phrase should describe what changed and practical impact in simple language, before method names. Let me craft. Comment content in pt-BR: "Esta correção é pontual e faz sentido, mas mais uma vez a regra de tela é resolvida dentro de um `<script>` com milhares de linhas embutido no template do modal (evSyncDescaracterStageUi já nasce nele e agora ganha mais dois pontos de chamada). Isso dificulta testar e reaproveitar essa lógica fora desta tela. Como o padrão do projeto manda colocar lógica de AJAX/montagem de card em public/js/, vale registrar uma dívida para migrar este bloco para um arquivo JS dedicado, em vez de continuar acumulando chamadas no template." Hmm, severity low; category maintainability. It should be located at one of the added lines. The code_comment tool requires existing_code that matches a newly added line, e.g., `evSyncDescaracterStageUi();`. Both new lines are identical. If I attach to the first occurrence at 2709, tool will locate first match of the added block probably. But duplicate identical lines could cause ambiguity. The diff contains two added identical lines. Existing code matching might pick one. It may still position correctly given sliding window, likely first occurrence. If both are considered, it's okay. Wait, but is that comment useful? It references the larger pattern not the new code; rules say focus on newly added code. I might be over-thinking. Let me think about a possible real functional nuance that we could legitimately report: Consider that in creation mode the `.ev-inj-descaracter` section is inside the expanded form of the card; the fix makes it visible (d-none removed) as soon as the card is created, even if the type selection happens AFTER creating the card... but sync uses current type. Suppose the flow where the user first picks "Acidente com Pessoa" then adds person—works. Now what about when the modal is reopened for "edição" of an AP event in `general` step: `evRenderInjuredPersonBoxes` creates cards before data restoration of type? If evSelectedType returns the right value (select pre-set from event), block becomes visible on cards. Good. I think final: report 0-1 findings. I'll choose to NOT over-flag god template for a 2-line hook and instead give a minimal, honest review conclusion? But wait, user-specific rules clearly prioritize it. Actually I think we can mention it but keep low severity. The problem: tool requires an existing_code snippet to anchor the comment; it must be newly added code lines. The two new lines `evSyncDescaracterStageUi();` will be anchored. Alternatively, I could anchor on the line `evSyncDescaracterStageUi();` at 2826 and note redundancy? No, contradiction with repo rule. Decision: Provide a low severity maintainability comment about embedding logic in template (god template), anchored to first new line. Keep concise. Hold on. Let me reconsider whether the "god template" finding is even within scope of the "newly added code" and whether the user expects it. The user's checklist includes "Revisar regressões funcionais, segurança, isolamento por empresa, integridade de dados, migrations, testes e problemas introduzidos pelo diff." The god template is not among them but is in user-specific mandatory rules. The instructions say "Focus on clarity, practicality, and comprehensiveness". Also "God template — maior peso... qualquer PR que aumente ainda mais essa mistura deve ser sinalizada antes de qualquer outro achado." Since we have no other findings, at least one comment would show diligence. I'll add it. Wait — But is it a fair finding on a hotfix that adds 2 lines? There is nuance. Given the "Strict Focus Rules" mention review every file individually. Files: 1. `.opencodereview/rule.json` — I need to give it a pass. Keys spelling fine. 2. `_modal_event.html.twig` — pass. I'll give the low severity maintainability note on the template plus perhaps a comment that rule.json maps fine. Let me now formulate the comment. For `.opencodereview/rule.json`: check keys. The new entries use keys "path", "merge_system_rule", "rule" — all spelled correctly; values ignored. No comments. For the template: if we add a comment about the god template, phrasing per user rules: simple first sentence. Also note the file is huge (7.7k lines) and rule prioritizes. One more potential maintainability point: the new calls in two functions are duplicated (one inside function that is invoked by the other). But again, the doc says keep both. Skip. Let me formulate the comment in pt-BR: content: "A correção em si está coerente com o fluxo descrito (checkbox passa a ser sincronizado ao montar o card), mas ela acrescenta mais um ponto de orquestração de UI dentro do `<script>` gigante embutido no template (este arquivo tem ~7.7 mil linhas, misturando markup e lógica de tela). Manter chamadas como esta no template dificulta teste unitário, reaproveitamento e manutenção, e o padrão do projeto manda esse tipo de lógica ir para `public/js/`. Não é bloqueante para este hotfix de 2 linhas, mas vale registrar dívida técnica de migrar `evSyncDescaracterStageUi` e os handlers de card para um JS dedicado na próxima mexida no fluxo." Category maintainability, severity low. Actually, wait — I need to double-check the target comment format: no more than concise. I'd anchor existing_code: " evSyncDescaracterStageUi();" (8 spaces indentation). Both added lines are identical: `+ evSyncDescaracterStageUi();`. The tool requires the code snippet from new lines with consistent style. Both lines have exactly 8 spaces indent (they're inside function bodies). Provide one comment. Let me make sure of the exact indentation from diff: ``` + evSyncDescaracterStageUi(); ``` 8 spaces. Will the sliding window match the correct one? It may match either of the two. Fine. Now, is severity low appropriate? yes. Also, maybe a note: The function also calls querySelectorAll over document each time; for each card inside evRenderInjuredPersonBoxes this runs N times + 1. That could be a "performance" micro. Since N small, skip. I'll go with just this one low severity maintainability note? Hmm, but the code_comment anchor duplicates. Let me perhaps place the comment with existing_code exactly at both lines? Provide a single comment item; the tool will match the first. Also, I might add a second comment about the redundancy in `evRenderInjuredPersonBoxes` where the extra call duplicates because evCreateInjuredPersonCard now syncs per card — as a performance/micro and maintainability note, severity low. But given repo doc explicitly instructs both calls and says don't treat as unnecessary, better not. Final: one low severity comment and then task_done. Actually, let me reconsider adding no comment at all and just approve via task_done. There's a tension: reviewers shouldn't manufacture issues. However, the user-specific rule has explicit priorities, and instructing "qualquer PR que aumente ainda mais essa mistura deve ser sinalizada antes de qualquer outro achado" - this PR does add more code to the embedded script (2 calls). This is a legitimate, if low, observation that the user themselves flagged as top priority. Given it's the only candidate, adding it as low severity is reasonable and aligned with the user's review guidelines. Let me produce it.
Analisei os dois arquivos do grupo de revisão. Vou consolidar o que encontrei. **`.opencodereview/rule.json`** — As duas novas entradas usam as mesmas chaves das existentes (`path`, `merge_system_rule`, `rule`), sem erro de grafia; o arquivo de regra referenciado existe. Sem problemas. **`templates/ssma/occurrence/partials/_modal_event.html.twig`** — As duas chamadas a `evSyncDescaracterStageUi()` foram adicionadas corretamente: uma ao final de `evCreateInjuredPersonCard()` (cobrindo também o caminho que passa por `evSyncInjuredCardsFromInvolved`, fluxo principal via "Pessoas envolvidas") e outra ao final de `evRenderInjuredPersonBoxes()`. A função está no mesmo bloco `<script>` (hoisting ok), é idempotente, não há risco de recursão nem de exposição de dado. O comportamento para outros tipos (ROS, Ambiental, etc.) permanece oculto porque o toggle depende de `evSelectedType() === 'ACIDENTE_PESSOAL'`. Registrando apenas um apontamento de manutenibilidade de prioridade baixa, conforme regra de revisão deste repositório:
Tool Calls (1)
code_comment
Show Details
{"comments": [{"content": "A correção é coerente com o fluxo descrito, mas reforça o padrão de orquestração de UI dentro do `<script>` gigante embutido no template (arquivo com ~7,7 mil linhas misturando markup e lógica de tela). Isso dificulta teste unitário, reaproveitamento e manutenção, e o padrão do projeto manda lógica de montagem de card/AJAX para `public/js/`. Não bloqueia este hotfix de 2 linhas, mas vale registrar a dívida de migrar `evSyncDescaracterStageUi` e os handlers de card para um JS dedicado na próxima alteração neste fluxo.", "existing_code": "        evSyncDescaracterStageUi();", "category": "maintainability", "severity": "low", "path": "templates/ssma/occurrence/partials/_modal_event.html.twig"}]}
(async)
Request #17 deepseek-v4-flash P:44.38K C:57 CR:44.29K CW:0 1372ms
Reasoning
Comment added. Now finalize with task_done.
Tool Calls (1)
task_done
Show Details
{"state": "DONE"}
Request #18 deepseek-v4-flash P:6.28K C:889 CR:6.02K CW:0 5281ms
Reasoning
Let me analyze the review task. I need to review files in `<review_files>`: 1. `.opencodereview/rule.json` - JSON file check for spelling errors in json-keys 2. `templates/ssma/occurrence/partials/_modal_event.html.twig` - Check the two added calls to `evSyncDescaracterStageUi()` There's a confirmed finding already for #1 (the god template issue). I should not repeat it. Let me look at the actual changes. The change adds two calls: - At end of `evCreateInjuredPersonCard()`: `evSyncDescaracterStageUi();` - At end of `evRenderInjuredPersonBoxes()`: `evSyncDescaracterStageUi();` Let me examine the context around these lines to understand if the function exists and is called correctly. Let me first check the JSON file for spelling errors in json keys. The diff adds: ```json { "path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "merge_system_rule": true, "rule": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md" }, { "path": "templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig", "merge_system_rule": true, "rule": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md" }, ``` Keys: path, merge_system_rule, rule - all consistent with existing entries. No spelling errors in keys. Values are content - ignore. Wait - the diff for the twig file shows changes in `_modal_event.html.twig`, and the rule.json references both `_modal_event.html.twig` and `_ev_injured_person_box.html.twig`. But the review group only contains `_modal_event.html.twig` (the actual code change file). The `_ev_injured_person_box.html.twig` rule registration references a rule file but that file isn't in review group. That's fine - cross-file observation within review files. Hmm, wait, the rule.json references a rule `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` for both `_modal_event.html.twig` and `_ev_injured_person_box.html.twig`. Let me check whether that rule file exists. The background mentions the commit includes `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` (3 files altered: json=1, md=1, twig=1). But the rule file isn't in the review group. However per instructions, context tools are for gathering background info only; my comments must address code within review files. Let me investigate the twig file changes more. Let me read the context around lines 2706 and 2823 to check: 1. That `evSyncDescaracterStageUi` is defined before use (function hoisting makes this moot if it's a function declaration). 2. Whether the function exists at all. 3. Correctness of calling it in these two places. Let me search for `evSyncDescaracterStageUi` in the file. Let me also check whether `evCreateInjuredPersonCard` and `evRenderInjuredPersonBoxes` exist. Let me start reading.
Tool Calls (2)
code_search
Show Details
{"search_text": "evSyncDescaracterStageUi"}
File: .opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md
Match lines: 4
6|- `templates/ssma/occurrence/partials/_modal_event.html.twig` — `evSyncDescaracterStageUi`, `evCreateInjuredPersonCard`, `evRenderInjuredPersonBoxes`, CSS `.ev-inj-descaracter*`
17|A #677 liberou o bloco na criação, mas o fluxo tipo → pessoa → card recriava o card com `d-none` sem novo sync — checkbox invisível. Esta PR chama `evSyncDescaracterStageUi()` após montar/atualizar cards.
32|- `.ev-inj-descaracter` continua oculto (`!isAp` em `evSyncDescaracterStageUi`).
41|`evSyncDescaracterStageUi()` deve ser chamado ao final de:

File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 8
2709|        evSyncDescaracterStageUi();
2826|        evSyncDescaracterStageUi();
2917|    function evSyncDescaracterStageUi() {
2947|        evSyncDescaracterStageUi();
3323|        evSyncDescaracterStageUi();
3398|        evSyncDescaracterStageUi();
5288|        evSyncDescaracterStageUi();
5736|                evSyncDescaracterStageUi();

file_read
Show Details
{"file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2600, "end_line": 2900}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2600-2900
2600|                if (el.classList.contains('ev-inj-had-injury') && typeof window.evSyncHadInjuryUi === 'function') {
2601|                    window.evSyncHadInjuryUi();
2602|                }
2603|                if (el.classList.contains('ev-inj-injury-classification')) {
2604|                    evSyncCardCatFromClassification(card);
2605|                }
2606|                if (el.classList.contains('ev-inj-consequence')) {
2607|                    evSyncCardDerivedSeverity(card);
2608|                    evSyncCardPotentialGteReal(card);
2609|                    evSyncInjuryFieldsByConsequence();
2610|                }
2611|                if (el.classList.contains('ev-inj-potential-consequence')) {
2612|                    evSyncCardPotentialGteReal(card);
2613|                }
2614|                evUpdateInjuredCardSummary(card);
2615|                evSyncInjuredPersonDetailsHidden();
2616|                evPersistDraftSoon();
2617|            });
2618|            el.addEventListener('input', function () {
2619|                evSyncInjuredPersonDetailsHidden();
2620|                evPersistDraftSoon();
2621|            });
2622|        });
2623|
2624|        card.querySelectorAll('.ev-inj-descaracter-btn').forEach(function (btn) {
2625|            btn.addEventListener('click', function () {
2626|                var val = this.getAttribute('data-descaracter-val');
2627|                if (val === '1' && !evCanDescharacterizeAccident()) {
2628|                    return;
2629|                }
2630|                card.setAttribute('data-descaracterizado', val);
2631|                var hidden = document.getElementById('ev_descaracterizado');
2632|                if (hidden) hidden.value = val;
2633|                var toggle = card.querySelector('.ev-inj-descaracter-toggle');
2634|                if (toggle) toggle.classList.remove('is-invalid');
2635|                evSyncDescaracterUi();
2636|                evSyncInjuredPersonDetailsHidden();
2637|                evPersistDraftSoon();
2638|            });
2639|        });
2640|        card.querySelectorAll('.ev-inj-descaracter-comment').forEach(function (ta) {
2641|            ta.addEventListener('input', function () {
2642|                ta.classList.remove('is-invalid');
2643|            });
2644|        });
2645|        evFilterCardConsequences(card);
2646|        evSyncCardCatFromClassification(card);
2647|        evSyncDescaracterUi();
2648|        if (window.jQuery && window.jQuery.fn.tooltip) {
2649|            window.jQuery(card).find('.ev-inj-descaracter-tip').tooltip({ container: 'body' });
2650|        }
2651|    }
2652|
2653|    function evCreateInjuredPersonCard(personId, saved, expand) {
2654|        var wrap = document.getElementById('ev_injured_person_boxes');
2655|        var tpl = document.getElementById('ev-injured-person-box-tpl');
2656|        if (!wrap || !tpl || !personId) return null;
2657|        var safe = String(personId).replace(/"/g, '');
2658|        var existing = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2659|        if (existing) return existing;
2660|
2661|        var source = tpl.querySelector('.ev-injured-person-box') || tpl.firstElementChild;
2662|        if (!source) return null;
2663|        var card = source.cloneNode(true);
2664|        if (!card || !card.classList) return null;
2665|        card.classList.remove('d-none');
2666|
2667|        var involved = evGetPeopleInvolved();
2668|        var sel = document.getElementById('ev_person_id');
2669|        var opt = sel ? sel.querySelector('option[value="' + safe + '"]') : null;
2670|        var name = involved.names[personId]
2671|            || (opt ? String(opt.text || '').trim() : '')
2672|            || ('#' + personId);
2673|
2674|        card.setAttribute('data-person-id', String(personId));
2675|        var nameEl = card.querySelector('.ev-inj-person-name');
2676|        if (nameEl) nameEl.textContent = name;
2677|        var sumName = card.querySelector('.js-ev-inj-summary-name');
2678|        if (sumName) sumName.textContent = name;
2679|
2680|        saved = saved || {};
2681|        if (!saved.attendance_date) saved.attendance_date = evTodayDateInputValue();
2682|        if (saved.had_injury === undefined) saved.had_injury = true;
2683|        evApplyCardInjuryData(card, saved);
2684|
2685|        evRenderInjuredPersonSummary(card, personId);
2686|
2687|        wrap.appendChild(card);
2688|        evBindInjuredCardEvents(card);
2689|
2690|        if (expand) {
2691|            evExpandInjuredCard(card);
2692|        } else {
2693|            evCollapseInjuredCard(card, true);
2694|            if (evCurrentStep === 'aprofundamento'
2695|                && evSelectedType() === 'ACIDENTE_PESSOAL'
2696|                && !wrap.querySelector('.ev-injured-person-box.is-expanded')) {
2697|                evExpandInjuredCard(card);
2698|            }
2699|        }
2700|        if (evCurrentStep === 'aprofundamento' && evSelectedType() === 'ACIDENTE_PESSOAL') {
2701|            if (typeof syncInjuryFieldsForCard === 'function') {
2702|                syncInjuryFieldsForCard(card);
2703|            }
2704|            if (typeof evFilterCardConsequences === 'function') {
2705|                evFilterCardConsequences(card);
2706|            }
2707|        }
2708|        evEnsurePrimaryInjuredMarked();
2709|        evSyncDescaracterStageUi();
2710|        return card;
2711|    }
2712|
2713|    function evRemoveInjuredPersonBox(personId) {
2714|        var wrap = document.getElementById('ev_injured_person_boxes');
2715|        if (!wrap || !personId) return;
2716|        var safe = String(personId).replace(/"/g, '');
2717|        var card = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2718|        if (!card) return;
2719|        var wasPrimary = card.classList.contains('is-primary');
2720|        if (card.classList.contains('is-expanded')) {
2721|            evParkBodyMapBlock();
2722|            evClearCanonicalInjuryIds();
2723|        }
2724|        card.remove();
2725|        var details = evGetInjuredDetailsObj();
2726|        delete details[String(personId)];
2727|        evSetInjuredDetailsObj(details);
2728|        // Não auto-expande a próxima — todas ficam colapsadas até o usuário editar.
2729|        if (wasPrimary) {
2730|            var next = wrap.querySelector('.ev-injured-person-box[data-person-id]');
2731|            if (next) {
2732|                evSelectPrimaryInjuredPerson(next.getAttribute('data-person-id'));
2733|            } else {
2734|                var sel = document.getElementById('ev_person_id');
2735|                if (sel) sel.removeAttribute('data-primary-injured-id');
2736|                evSyncInjuredPickCircles('');
2737|            }
2738|        } else {
2739|            evSyncPrimaryInjuredPersonId();
2740|        }
2741|        evFilterInjuredPersonSelect();
2742|        evPersistDraftSoon();
2743|        evUpdateInjuredPersonsEmptyState();
2744|    }
2745|
2746|    /**
2747|     * Select oculto (legado): adicionar acidentado manualmente — fluxo principal = cards via Pessoas envolvidas.
2748|     */
2749|    var _evAddingInjuredLock = false;
2750|    function evAddInjuredPersonFromSelect() {
2751|        if (_evAddingInjuredLock) return;
2752|        var sel = document.getElementById('ev_person_id');
2753|        if (!sel) return;
2754|        var personId = String(sel.value || '');
2755|        if (!personId) return;
2756|
2757|        _evAddingInjuredLock = true;
2758|        try {
2759|            var wrap = document.getElementById('ev_injured_person_boxes');
2760|            var safe = personId.replace(/"/g, '');
2761|            var existing = wrap && wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2762|            if (existing) {
2763|                // Mantém colapsada; usuário edita pelo lápis.
2764|                evUpdateInjuredCardSummary(existing);
2765|            } else {
2766|                var saved = evGetInjuredDetailsObj()[personId] || {};
2767|                var created = evCreateInjuredPersonCard(personId, saved, false);
2768|                if (!created) {
2769|                    console.warn('[SSMA] Falha ao criar caixinha do acidentado', personId);
2770|                } else {
2771|                    evSyncInjuredPersonDetailsHidden();
2772|                }
2773|            }
2774|            // Limpa o select sem reentrar (setCustomSelectValue dispara change).
2775|            sel.value = '';
2776|            if (typeof window.setCustomSelectValue === 'function') {
2777|                window.setCustomSelectValue('ev_person_id', '');
2778|            } else {
2779|                evSyncCustomSelectVisual('ev_person_id', true);
2780|            }
2781|            evSyncPrimaryInjuredPersonId();
2782|            evFilterInjuredPersonSelect();
2783|        } finally {
2784|            _evAddingInjuredLock = false;
2785|        }
2786|    }
2787|
2788|    /** Remonta caixinhas a partir de injured_person_details (+ person_id legado). */
2789|    function evRenderInjuredPersonBoxes() {
2790|        var wrap = document.getElementById('ev_injured_person_boxes');
2791|        if (!wrap) return;
2792|        var details = evGetInjuredDetailsObj();
2793|        var ids = Object.keys(details);
2794|        var primary = '';
2795|        var sel = document.getElementById('ev_person_id');
2796|        if (sel && sel.value) primary = String(sel.value);
2797|        if (!primary && sel && sel.getAttribute('data-primary-injured-id')) {
2798|            primary = String(sel.getAttribute('data-primary-injured-id') || '');
2799|        }
2800|
2801|        if (primary && !details[primary]) {
2802|            details[primary] = { attendance_date: evTodayDateInputValue(), had_injury: true, body_parts: [] };
2803|            evSetInjuredDetailsObj(details);
2804|            ids = Object.keys(details);
2805|        }
2806|
2807|        evParkBodyMapBlock();
2808|        evClearCanonicalInjuryIds();
2809|        wrap.innerHTML = '';
2810|
2811|        ids.forEach(function (pid) {
2812|            // Sempre cria colapsada; expandir só via lápis (ou validação).
2813|            evCreateInjuredPersonCard(pid, details[pid] || {}, false);
2814|        });
2815|        // Respeita primary já salvo; senão marca o primeiro.
2816|        var preferred = primary || '';
2817|        if (preferred && wrap.querySelector('.ev-injured-person-box[data-person-id="' + preferred.replace(/"/g, '') + '"]')) {
2818|            evSelectPrimaryInjuredPerson(preferred);
2819|        } else {
2820|            evEnsurePrimaryInjuredMarked();
2821|        }
2822|        evSyncInjuredPersonDetailsHidden();
2823|        evSyncPrimaryInjuredPersonId();
2824|        evFilterInjuredPersonSelect();
2825|        evUpdateInjuredPersonsEmptyState();
2826|        evSyncDescaracterStageUi();
2827|        if (evCurrentStep === 'aprofundamento' && evSelectedType() === 'ACIDENTE_PESSOAL') {
2828|            window.requestAnimationFrame(function () {
2829|                if (typeof evEnsurePrimaryInjuredCardExpanded === 'function') {
2830|                    evEnsurePrimaryInjuredCardExpanded();
2831|                }
2832|            });
2833|        }
2834|    }
2835|    var evDraftTimer = null;
2836|    function evPersistDraftSoon() {
2837|        if (evDraftTimer) clearTimeout(evDraftTimer);
2838|        evDraftTimer = setTimeout(evPersistDraftNow, 400);
2839|    }
2840|    function evPersistDraftNow() {
2841|        var mode = (document.getElementById('ev_form_mode') || {}).value || 'create';
2842|        if (mode !== 'create') return;
2843|        try {
2844|            var form = document.getElementById('form-event-new');
2845|            if (!form) return;
2846|            var data = {};
2847|            Array.prototype.forEach.call(form.elements, function (el) {
2848|                if (!el.name && !el.id) return;
2849|                var key = el.id || el.name;
2850|                if (!key) return;
2851|                if (el.type === 'checkbox' || el.type === 'radio') {
2852|                    data[key] = !!el.checked;
2853|                } else {
2854|                    data[key] = el.value;
2855|                }
2856|            });
2857|            data.__people_ids = (document.getElementById('ev_people_ids') || {}).value || '';
2858|            data.__witness_ids = (document.getElementById('ev_witness_ids') || {}).value || '';
2859|            data.__injured_details = (document.getElementById('ev_injured_person_details') || {}).value || '';
2860|            sessionStorage.setItem(EV_DRAFT_KEY, JSON.stringify(data));
2861|        } catch (e) {}
2862|    }
2863|    function evClearDraft() {
2864|        try { sessionStorage.removeItem(EV_DRAFT_KEY); } catch (e) {}
2865|    }
2866|    function evRestoreDraftIfAny() {
2867|        try {
2868|            var raw = sessionStorage.getItem(EV_DRAFT_KEY);
2869|            if (!raw) return false;
2870|            var data = JSON.parse(raw);
2871|            if (!data || typeof data !== 'object') return false;
2872|            Object.keys(data).forEach(function (key) {
2873|                if (key.indexOf('__') === 0) return;
2874|                var el = document.getElementById(key);
2875|                if (!el) return;
2876|                if (el.type === 'checkbox' || el.type === 'radio') {
2877|                    el.checked = !!data[key];
2878|                } else {
2879|                    el.value = data[key];
2880|                }
2881|            });
2882|            if (data.__people_ids) {
2883|                var pe = document.getElementById('ev_people_ids');
2884|                if (pe) pe.value = data.__people_ids;
2885|            }
2886|            if (data.__witness_ids) {
2887|                var we = document.getElementById('ev_witness_ids');
2888|                if (we) we.value = data.__witness_ids;
2889|            }
2890|            if (data.__injured_details) {
2891|                var ie = document.getElementById('ev_injured_person_details');
2892|                if (ie) ie.value = data.__injured_details;
2893|            }
2894|            // Remonta caixinhas após restaurar o JSON do draft.
2895|            setTimeout(function () {
2896|                if (typeof evRenderInjuredPersonBoxes === 'function') evRenderInjuredPersonBoxes();
2897|            }, 0);
2898|            return true;
2899|        } catch (e) {
2900|            return false;
Request #19 deepseek-v4-flash P:11.43K C:209 CR:7.17K CW:0 1768ms
Reasoning
Let me look at the function `evSyncDescaracterStageUi` definition at line 2917 to understand its behavior and any dependencies.
Tool Calls (2)
file_read
Show Details
{"end_line": 2960, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2905}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2905-2960
2905|        return window.SSMA_CAN_DESCARACTERIZE === true || window.SSMA_CAN_DESCARACTERIZE === 'true';
2906|    }
2907|
2908|    function evIsDescaracterSuspectChecked() {
2909|        var el = document.getElementById('ev_descaracter_suspect');
2910|        return !!(el && el.checked);
2911|    }
2912|
2913|    function evIsCreateMode() {
2914|        return ((document.getElementById('ev_form_mode') || { value: 'create' }).value === 'create');
2915|    }
2916|
2917|    function evSyncDescaracterStageUi() {
2918|        var isAp = evSelectedType() === 'ACIDENTE_PESSOAL';
2919|        var isDoctorAprof = !!evAprofundamentoOnlyMode;
2920|        var form = document.getElementById('form-event-new');
2921|        if (form) form.classList.toggle('is-doctor-aprof', isDoctorAprof);
2922|        // Checkbox global de suspeita (etapa 1) fica sempre oculto — suspeita agora é por card no aprofundamento.
2923|        var suspectWrap = document.getElementById('ev-suspeita-wrap');
2924|        if (suspectWrap) {
2925|            suspectWrap.classList.add('d-none');
2926|        }
2927|        document.querySelectorAll('.ev-inj-descaracter').forEach(function (el) {
2928|            // Seção de descaracterização aparece sempre que é Acidente Pessoal (criação e aprofundamento).
2929|            // Na criação: só o checkbox de suspeita fica visível (Sim/Não oculto via CSS fora do aprofundamento).
2930|            el.classList.toggle('d-none', !isAp);
2931|        });
2932|        // Sincroniza Sim/Não de cada card com o estado do checkbox de suspeita do próprio card.
2933|        document.querySelectorAll('.ev-injured-person-box').forEach(function (card) {
2934|            var suspChk = card.querySelector('.ev-inj-suspect-chk');
2935|            var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap');
2936|            if (suspChk && yesNoWrap) {
2937|                yesNoWrap.classList.toggle('d-none', !suspChk.checked);
2938|            }
2939|        });
2940|    }
2941|
2942|    function evSyncDescaracterUi() {
2943|        var hidden = document.getElementById('ev_descaracterizado');
2944|        var canSn = evCanDescharacterizeAccident();
2945|        var cur = String((hidden || {}).value || '');
2946|
2947|        evSyncDescaracterStageUi();
2948|
2949|        // Pré-seleciona "Sim" quando o profissional não tem permissão de descaracterização
2950|        // e o campo ainda está vazio. Ele só pode dizer "Sim" de qualquer forma, então
2951|        // pré-preencher evita a validação falhar sem o usuário perceber o que falta.
2952|        if (!canSn && cur === '' && evAprofundamentoOnlyMode && hidden) {
2953|            hidden.value = '0';
2954|            cur = '0';
2955|            document.querySelectorAll('.ev-injured-person-box').forEach(function (c) {
2956|                c.setAttribute('data-descaracterizado', '0');
2957|            });
2958|        }
2959|
2960|        document.querySelectorAll('.ev-injured-person-box').forEach(function (card) {
file_read
Show Details
{"end_line": 2806, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 2706}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 2706-2806
2706|            }
2707|        }
2708|        evEnsurePrimaryInjuredMarked();
2709|        evSyncDescaracterStageUi();
2710|        return card;
2711|    }
2712|
2713|    function evRemoveInjuredPersonBox(personId) {
2714|        var wrap = document.getElementById('ev_injured_person_boxes');
2715|        if (!wrap || !personId) return;
2716|        var safe = String(personId).replace(/"/g, '');
2717|        var card = wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2718|        if (!card) return;
2719|        var wasPrimary = card.classList.contains('is-primary');
2720|        if (card.classList.contains('is-expanded')) {
2721|            evParkBodyMapBlock();
2722|            evClearCanonicalInjuryIds();
2723|        }
2724|        card.remove();
2725|        var details = evGetInjuredDetailsObj();
2726|        delete details[String(personId)];
2727|        evSetInjuredDetailsObj(details);
2728|        // Não auto-expande a próxima — todas ficam colapsadas até o usuário editar.
2729|        if (wasPrimary) {
2730|            var next = wrap.querySelector('.ev-injured-person-box[data-person-id]');
2731|            if (next) {
2732|                evSelectPrimaryInjuredPerson(next.getAttribute('data-person-id'));
2733|            } else {
2734|                var sel = document.getElementById('ev_person_id');
2735|                if (sel) sel.removeAttribute('data-primary-injured-id');
2736|                evSyncInjuredPickCircles('');
2737|            }
2738|        } else {
2739|            evSyncPrimaryInjuredPersonId();
2740|        }
2741|        evFilterInjuredPersonSelect();
2742|        evPersistDraftSoon();
2743|        evUpdateInjuredPersonsEmptyState();
2744|    }
2745|
2746|    /**
2747|     * Select oculto (legado): adicionar acidentado manualmente — fluxo principal = cards via Pessoas envolvidas.
2748|     */
2749|    var _evAddingInjuredLock = false;
2750|    function evAddInjuredPersonFromSelect() {
2751|        if (_evAddingInjuredLock) return;
2752|        var sel = document.getElementById('ev_person_id');
2753|        if (!sel) return;
2754|        var personId = String(sel.value || '');
2755|        if (!personId) return;
2756|
2757|        _evAddingInjuredLock = true;
2758|        try {
2759|            var wrap = document.getElementById('ev_injured_person_boxes');
2760|            var safe = personId.replace(/"/g, '');
2761|            var existing = wrap && wrap.querySelector('.ev-injured-person-box[data-person-id="' + safe + '"]');
2762|            if (existing) {
2763|                // Mantém colapsada; usuário edita pelo lápis.
2764|                evUpdateInjuredCardSummary(existing);
2765|            } else {
2766|                var saved = evGetInjuredDetailsObj()[personId] || {};
2767|                var created = evCreateInjuredPersonCard(personId, saved, false);
2768|                if (!created) {
2769|                    console.warn('[SSMA] Falha ao criar caixinha do acidentado', personId);
2770|                } else {
2771|                    evSyncInjuredPersonDetailsHidden();
2772|                }
2773|            }
2774|            // Limpa o select sem reentrar (setCustomSelectValue dispara change).
2775|            sel.value = '';
2776|            if (typeof window.setCustomSelectValue === 'function') {
2777|                window.setCustomSelectValue('ev_person_id', '');
2778|            } else {
2779|                evSyncCustomSelectVisual('ev_person_id', true);
2780|            }
2781|            evSyncPrimaryInjuredPersonId();
2782|            evFilterInjuredPersonSelect();
2783|        } finally {
2784|            _evAddingInjuredLock = false;
2785|        }
2786|    }
2787|
2788|    /** Remonta caixinhas a partir de injured_person_details (+ person_id legado). */
2789|    function evRenderInjuredPersonBoxes() {
2790|        var wrap = document.getElementById('ev_injured_person_boxes');
2791|        if (!wrap) return;
2792|        var details = evGetInjuredDetailsObj();
2793|        var ids = Object.keys(details);
2794|        var primary = '';
2795|        var sel = document.getElementById('ev_person_id');
2796|        if (sel && sel.value) primary = String(sel.value);
2797|        if (!primary && sel && sel.getAttribute('data-primary-injured-id')) {
2798|            primary = String(sel.getAttribute('data-primary-injured-id') || '');
2799|        }
2800|
2801|        if (primary && !details[primary]) {
2802|            details[primary] = { attendance_date: evTodayDateInputValue(), had_injury: true, body_parts: [] };
2803|            evSetInjuredDetailsObj(details);
2804|            ids = Object.keys(details);
2805|        }
2806|
Request #20 deepseek-v4-flash P:13.88K C:3.15K CR:11.52K CW:0 20643ms
Reasoning
Now let me analyze the change. The PR adds `evSyncDescaracterStageUi()` at the end of `evCreateInjuredPersonCard()` and `evRenderInjuredPersonBoxes()`. `evCreateInjuredPersonCard` calls `evSyncDescaracterStageUi()` at line 2709. Then `evRenderInjuredPersonBoxes` iterates calling `evCreateInjuredPersonCard` for each person and calls `evSyncDescaracterStageUi()` again at the end (line 2826). So if there are N injured persons, evRenderInjuredPersonBoxes will call evSyncDescaracterStageUi N+1 times. That's minor redundancy/performance but not critical. Let me examine `evSyncDescaracterStageUi` more closely: ```js function evSyncDescaracterStageUi() { var isAp = evSelectedType() === 'ACIDENTE_PESSOAL'; var isDoctorAprof = !!evAprofundamentoOnlyMode; var form = document.getElementById('form-event-new'); if (form) form.classList.toggle('is-doctor-aprof', isDoctorAprof); // Checkbox global de suspeita (etapa 1) fica sempre oculto — suspeita agora é por card no aprofundamento. var suspectWrap = document.getElementById('ev-suspeita-wrap'); if (suspectWrap) { suspectWrap.classList.add('d-none'); } document.querySelectorAll('.ev-inj-descaracter').forEach(function (el) { el.classList.toggle('d-none', !isAp); }); // Sincroniza Sim/Não de cada card com o estado do checkbox de suspeita do próprio card. document.querySelectorAll('.ev-injured-person-box').forEach(function (card) { var suspChk = card.querySelector('.ev-inj-suspect-chk'); var yesNoWrap = card.querySelector('.ev-inj-descaracter-yesno-wrap'); if (suspChk && yesNoWrap) { yesNoWrap.classList.toggle('d-none', !suspChk.checked); } }); } ``` Wait, the requirement says: - On creation, only the checkbox "Há suspeita de descaracterização do acidente?" should be visible (Sim/Não hidden). - Sim/Não and comment only in "Aprofundamento técnico". But the function `evSyncDescaracterStageUi` toggles `.ev-inj-descaracter` based only on `isAp` (Acidente Pessoal), meaning the whole section is visible in creation. The "Sim/Não oculto via CSS fora do aprofundamento" comment suggests CSS handles hiding of Sim/Não in creation mode. That's pre-existing behavior, not part of this PR. This PR is about ensuring the checkbox is visible when cards are created after type is chosen and person is added. This is a functional fix. The key question - are there bugs introduced? Potential issue: When `evCreateInjuredPersonCard` is called during `evRenderInjuredPersonBoxes` loop (creating multiple cards), `evSyncDescaracterStageUi()` is called after each card creation. Since the function calls `document.querySelectorAll('.ev-inj-descaracter')`, each call after each card creation works fine. Performance-wise it's fine for small N. Another question: does calling `evSyncDescaracterStageUi` here have any side effects that could be problematic in contexts where evRenderInjuredPersonBoxes is invoked? For instance in aprofundamento mode, in edit mode etc. Since the function only toggles visibility classes, this seems low-risk. Is there any context where `evCreateInjuredPersonCard` is called and `evSelectedType()` might not be ready? Hmm — `evSelectedType()` reads a select value, returns something like `''`. If the type isn't selected yet, `isAp` is false, so the block gets hidden (`d-none` added). When the type is later chosen, presumably an on-change handler calls `evSyncDescaracterStageUi` again (which is the pre-existing sync). So this seems fine. Potential correctness: In `evCreateInjuredPersonCard`, the call to `evSyncDescaracterStageUi` happens right before `return card`. That ensures newly added cards with the `.ev-inj-descaracter` block are visible. Now let me consider redundancy: In `evRenderInjuredPersonBoxes`, for each `pid` we call `evCreateInjuredPersonCard`, which invokes `evSyncDescaracterStageUi()`. Then at the end, there's an extra call. If there are e.g. 10 people, `evSyncDescaracterStageUi` runs 11 times, each with multiple `querySelectorAll` over the whole document. Not a major perf concern but it's redundant. This is minor. Could mention as low priority maintainability? The confirmed finding #1 already covered the god-template issue. We should avoid repeating. Hmm, but the redundancy: since the added line in `evRenderInjuredPersonBoxes` is made redundant because `evCreateInjuredPersonCard` already calls `evSyncDescaracterStageUi()` after each card creation. Actually wait — is the last line needed? Yes, at the end of evRenderInjuredPersonBoxes, evSelectPrimaryInjuredPerson / evEnsurePrimaryInjuredMarked are called after the loop, which might... no, those don't affect descaracter visibility. So the extra call at line 2826 is effectively redundant given the call at line 2709 for each created card. However, it's harmless and provides defense in depth if cards are created without the function (they aren't). But note: evCreateInjuredPersonCard has early returns `return null` if wrap/tpl/personId missing or existing card found. In the loop of evRenderInjuredPersonBoxes, if all pids already have cards... wait, wrap.innerHTML = '' clears before loop, so every call creates. OK. Is the added line in `evRenderInjuredPersonBoxes` redundant? Yes, given line 2709 already handles it. But redundancy isn't a bug. It's negligible. Should I report as maintainability low? Given the user-specific rules: "Achado de estilo puro ... sem efeito real deve ser curto e de prioridade baixa". The redundancy has negligible real effect. Might skip. Let me think about a bigger issue: In `evRenderInjuredPersonBoxes`, the function may be invoked in aprofundamento mode too (per line 2827 check `evCurrentStep === 'aprofundamento'`). When restoring in aprofundamento, card creation calls `evSyncDescaracterStageUi`, which toggles `.ev-inj-descaracter` visible for AP type. That is correct since the block should be visible in aprofundamento as well. But wait — the requirement says Sim/Não is only in aprofundamento; creation only checkbox. The `evSyncDescaracterStageUi` function toggles yesNoWrap hidden based on suspChk.checked regardless of mode. In aprofundamento the Sim/Não should be visible even if the suspect checkbox is not checked? Hmm — let's read the rule doc to understand expected behavior. Actually, wait. The description in the requirement: "#677 liberou o checkbox "Há suspeita de descaracterização do acidente?" na criação, mas na prática ele não aparecia." And: "Sim/Não e comentário continuam só no Aprofundamento técnico (regra da #677)." Let me read the rule doc `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` to understand details. But that file is outside review group; still, context gathering is allowed. Let me think about whether these calls could break aprofundamento. In `evSyncDescaracterStageUi`, for each `.ev-injured-person-box` card, if `suspChk` exists and unchecked, the yesNoWrap gets hidden (`d-none`). In aprofundamento, the suspect checkbox may be unchecked but Sim/Não should still be visible? Let's read more context around how yesNoWrap works in aprofundamento. Actually this is pre-existing behavior of `evSyncDescaracterStageUi` (already called on type change etc.). The change merely adds additional call sites. The function body isn't being modified in this PR. So unless a new call site triggers it in a state where it previously wasn't run (causing side effects), there isn't a new bug. Consider `evCreateInjuredPersonCard` is also called in aprofundamento mode when adding people? Possibly, in aprofundamento there is also adding injured persons. When adding a new person card in aprofundamento, the call will now sync, which is correct. But let me look at possible edge case: When a user selects type ACIDENTE_PESSOAL first and then adds person via "Pessoas envolvidas", the modal may have `evCurrentStep` not yet set. evSyncDescaracterStageUi uses `evSelectedType()` and CSS classes; if type is already AP, isAp true → show block. Good. Now the concern is that `evSyncDescaracterStageUi` also toggles the `is-doctor-aprof` class on the form using `evAprofundamentoOnlyMode`. That's pre-existing. Fine. Now consider whether calling `evSyncDescaracterStageUi` in `evRenderInjuredPersonBoxes` could be executed when the DOM doesn't yet contain the injured boxes container (wrap). The function operates on document.querySelectorAll of `.ev-inj-descaracter`, global; that's fine. Potential bug: `evCreateInjuredPersonCard` might be called for a person while wrap is being rebuilt (`wrap.innerHTML = ''` already executed). Fine. Now think about ordering: In `evRenderInjuredPersonBoxes`, after the loop, `evSelectPrimaryInjuredPerson(preferred)` and `evEnsurePrimaryInjuredMarked()` are called, then the sync. The sync toggles yesNoWrap based on suspect checkbox checks. If a card has suspect checkbox already checked in saved data (from draft), the yesnoWrap would show. Actually in creation the Sim/Não should be hidden regardless. But this behavior pre-exists in the function; the CSS is said to hide Sim/Não outside aprofundamento. So no new bug. Wait — there is one subtle bug potential. Look at `evSyncDescaracterStageUi` line 2929 comment: "Na criação: só o checkbox de suspeita fica visível (Sim/Não oculto via CSS fora do aprofundamento)." So Sim/Não hidden via CSS when not in aprofundamento mode. But then lines 2933-2939 toggle `yesNoWrap` `d-none` based on the suspect checkbox checked state, for all modes (including aprofundamento). That means in aprofundamento, if the suspect checkbox is unchecked, Sim/Não is hidden. Is that intended? Requirement #636 says Sim/Não should work in aprofundamento as before. This is pre-existing function logic; not part of this PR. Not a new finding. OK. The actual changes are minimal (2 added function calls). The main confirmed finding was the god-template pattern, which I shouldn't repeat. Let me verify whether the two functions (evCreateInjuredPersonCard, evRenderInjuredPersonBoxes) can throw before reaching the new lines. Not really the concern. Now let me think about one thing: `evCreateInjuredPersonCard` is called within `evRenderInjuredPersonBoxes` in a forEach. Each iteration triggers a full `document.querySelectorAll('.ev-inj-descaracter')` and `.ev-injured-person-box`. So with N cards, evSyncDescaracterStageUi is executed N+1 times (once per card creation + one final explicit call). This O(N²) accumulation of querySelectorAll over whole doc isn't a real performance concern since N is small (people involved in an occurrence). Probably fine, but we could mention redundancy — the added line in evRenderInjuredPersonBoxes is redundant because evCreateInjuredPersonCard already calls the sync. It's not a bug though. Let me check whether there's a potential null-safety issue with the new call in `evCreateInjuredPersonCard` when called at line 2827 etc. `evSyncDescaracterStageUi` itself is robust (uses guarded access). However, there's one possible concern: `evCreateInjuredPersonCard` returns `null` early if there's an existing card; but then the new line wouldn't run for that return path. Fine. Is there any call site of `evCreateInjuredPersonCard` where the sync would be undesirable? E.g., when `evSyncInjuredPersonDetailsHidden` or other steps need to run before sync? No, sync only toggles visibility. Now check `evSelectedType` definition and `evAprofundamentoOnlyMode` to see possible errors (throwing). Let me search them. Actually, more interesting: Could the newly added call cause an error when called in a context where these helper vars are not yet defined? Since all are function declarations inside the same script (likely var/function scope), hoisting works. Let me confirm evSelectedType and evAprofundamentoOnlyMode definitions exist in this script. Let me search for evAprofundamentoOnlyMode and evSelectedType definitions.
Tool Calls (3)
code_search
Show Details
{"search_text": "function evSelectedType", "file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"]}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 1
1553|    function evSelectedType() {

code_search
Show Details
{"search_text": "evAprofundamentoOnlyMode", "file_patterns": ["templates/ssma/occurrence/partials/_modal_event.html.twig"]}
File: templates/ssma/occurrence/partials/_modal_event.html.twig
Match lines: 24
1548|    var evAprofundamentoOnlyMode = false;
2919|        var isDoctorAprof = !!evAprofundamentoOnlyMode;
2952|        if (!canSn && cur === '' && evAprofundamentoOnlyMode && hidden) {
3335|            draft.classList.toggle('d-none', !evAprofundamentoOnlyMode || evCurrentStep !== 'aprofundamento');
3337|        if (evAprofundamentoOnlyMode) {
3373|        if (evAprofundamentoOnlyMode) {
3381|            general.classList.toggle('is-readonly', !!evAprofundamentoOnlyMode);
3386|            if (evAprofundamentoOnlyMode) {
6020|            if (boxesWrap && evAprofundamentoOnlyMode) {
6136|        evAprofundamentoOnlyMode = false;
6530|            evAprofundamentoOnlyMode = true;
6538|            // Sincroniza a UI de descaracterização agora que evAprofundamentoOnlyMode=true.
6569|        evAprofundamentoOnlyMode = false;
6662|            if (evAprofundamentoOnlyMode) return;
6681|        if (evAprofundamentoOnlyMode && evIsAprofundamentoFinalized() && !EV_IS_ADMIN_APROFUNDAMENTO) {
6697|        var finalizeAprofundamento = !evAprofundamentoOnlyMode || !!evAprofundamentoFinalizeIntent;
6698|        if (evAprofundamentoOnlyMode) {
6704|        if (evCurrentStep === 'general' && !evAprofundamentoOnlyMode) {
6721|            if (!evAprofundamentoOnlyMode) {
6727|        var softAprofundamentoDraft = evAprofundamentoOnlyMode && !finalizeAprofundamento;
6964|        if (evCurrentStep === 'aprofundamento' && evAprofundamentoOnlyMode) {
7109|            if (!descFromCard && evAprofundamentoOnlyMode && !evCanDescharacterizeAccident()) {
7115|            if (descAnswered && evAprofundamentoOnlyMode) {
7126|            if (!evAprofundamentoOnlyMode) {

file_read
Show Details
{"file_path": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md"}
File: .opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md (Total lines: 63)
IS_TRUNCATED: false
LINE_RANGE: 1-63
1|#### Escopo desta regra
2|
3|Checkbox **“Há suspeita de descaracterização do acidente?”** no card do acidentado durante **Novo evento** (Acidente com Pessoa). Complementa a PR #636 (aprofundamento) e a #677 (exibir bloco na criação).
4|
5|Arquivos cobertos:
6|- `templates/ssma/occurrence/partials/_modal_event.html.twig` — `evSyncDescaracterStageUi`, `evCreateInjuredPersonCard`, `evRenderInjuredPersonBoxes`, CSS `.ev-inj-descaracter*`
7|- `templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig` — bloco `.ev-inj-descaracter` / `.ev-inj-suspect-chk`
8|
9|Fora de escopo: validar ocorrência / SMTP (#671), painel do Plano de Ação.
10|
11|---
12|
13|#### Problema de negócio
14|
15|Felipe/produto (Figma Etapa 8, `docs/ssma/ocorrencia-06-tudo-na-caixinha-descaracter.md`): o checkbox deve aparecer **no card do acidentado na criação**, não só no Aprofundamento técnico.
16|
17|A #677 liberou o bloco na criação, mas o fluxo tipo → pessoa → card recriava o card com `d-none` sem novo sync — checkbox invisível. Esta PR chama `evSyncDescaracterStageUi()` após montar/atualizar cards.
18|
19|---
20|
21|#### Comportamento esperado — bloqueante se quebrar
22|
23|**Novo evento (criação), tipo `ACIDENTE_PESSOAL`:**
24|- Ao adicionar pessoa e expandir o card, o checkbox de suspeita aparece abaixo de *“Oriente o profissional…”*.
25|- **Só** o checkbox fica visível; Sim/Não, comentário e “Sem permissão” permanecem ocultos (CSS `#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle` etc.).
26|- Marcar o checkbox atualiza `data-descaracter-suspect` no card e espelha em `#ev_descaracter_suspect` para o payload (`descaracter_suspect: 1`).
27|
28|**Aprofundamento técnico (`evAprofundamentoOnlyMode`):**
29|- Comportamento da #636/#677 inalterado: Sim/Não quando suspeita marcada; gate de permissão `SSMA_CAN_DESCARACTERIZE` / `evCanDescharacterizeAccident()`.
30|
31|**Outros tipos (ROS, Ambiental, Quase Acidente…):**
32|- `.ev-inj-descaracter` continua oculto (`!isAp` em `evSyncDescaracterStageUi`).
33|
34|**Checkbox global `#ev-suspeita-wrap` (etapa 1):**
35|- Permanece oculto — suspeita é por card, não no registro geral.
36|
37|---
38|
39|#### Sync obrigatório após montar card
40|
41|`evSyncDescaracterStageUi()` deve ser chamado ao final de:
42|- `evCreateInjuredPersonCard()`
43|- `evRenderInjuredPersonBoxes()`
44|
45|Remover essas chamadas faz o checkbox sumir no fluxo normal (tipo → pessoa → card). **Não** tratar como “código desnecessário”.
46|
47|---
48|
49|#### O que NÃO sinalizar como bloqueio nesta PR
50|
51|1. **Checkbox na criação vs só aprofundamento** — intencional por produto; não pedir para esconder de novo na criação.
52|2. **Sim/Não ausente na criação** — intencional; só no aprofundamento.
53|3. **Duplicar lógica da #636** — esta PR só garante visibilidade do checkbox após render do card.
54|
55|---
56|
57|#### Como testar
58|
59|1. Novo evento → Acidente com Pessoa → adicionar pessoa → expandir card → checkbox visível.
60|2. Marcar suspeita → Sim/Não **não** aparece na criação.
61|3. Salvar → Aprofundamento técnico → Sim/Não funciona com suspeita marcada.
62|4. Tipo ROS → bloco não aparece.
63|
Request #21 deepseek-v4-flash P:18.7K C:1.65K CR:16.9K CW:0 11845ms
Reasoning
Now I have a clearer picture. The rule doc explicitly says these calls are required. Good. Let me consider if there are real issues in the diff besides the confirmed god-template finding: 1. The rule.json addition references `_ev_injured_person_box.html.twig` under the rule file. JSON keys are `path`, `merge_system_rule`, `rule` — consistent spelling with existing entries. No spelling errors in keys. 2. The rule.json adds entries for files in the review group. Only `_modal_event.html.twig` is modified in this PR; `_ev_injured_person_box.html.twig` is not changed. Adding a rule mapping to an unchanged file is a configuration thing, not a functional problem. Actually wait — it says path `templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig` which is a file in the codebase. This just maps OCR rules. That's fine, since the rule doc covers that file. Now, check potential duplication/redundancy: In `evRenderInjuredPersonBoxes`, the explicit `evSyncDescaracterStageUi()` at line 2826 is redundant given each `evCreateInjuredPersonCard` call at line 2813 already triggers sync (line 2709). However, there's a subtle difference: evCreateInjuredPersonCard can early-return null if card creation fails, but in evRenderInjuredPersonBoxes after innerHTML='', all cards should be created. So the final call is mostly redundant but harmless — defense in depth. Not a report-worthy issue on its own. Wait — Actually, one nuance: evCreateInjuredPersonCard with early-return for existing card. Inside evRenderInjuredPersonBoxes, wrap.innerHTML='' cleared everything first, so each call creates a new card (returning card). So the end-of-function call is fully redundant. Not a bug. Let me think about possible regression: Adding the call in `evCreateInjuredPersonCard` means every time a card is created during normal "aprofundamento" render or edit-mode rendering, evSyncDescaracterStageUi is executed. In aprofundamento mode, evSyncDescaracterStageUi toggles yesNoWrap d-none based on suspChk.checked. That behavior already existed via other call sites (2947, 3323, 3398, 5288, 5736). So no change in semantics. Potential issue: In creation mode, if the user checks the suspect checkbox, `yesNoWrap` would be shown (d-none removed) because `evSyncDescaracterStageUi` line 2937 toggles based on suspChk.checked — but CSS `#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle` hides it anyway. The rule doc says "Sim/Não ausente na criação — intencional; só no aprofundamento" and CSS hides it. So fine. Is there a scenario where adding the call to evCreateInjuredPersonCard could run when type select element isn't available? evSelectedType reads DOM element. In evRenderInjuredPersonBoxes called from draft restore at any point; if the modal/type select is in DOM, fine. Now let me consider if `evCreateInjuredPersonCard` is also called in contexts where `evSyncDescaracterStageUi` would cause flicker or change visibility of existing global `.ev-inj-descaracter` blocks for OTHER cards based on current type. Suppose user changes type from ACIDENTE_PESSOAL to another while cards exist, adding... no. Actually, one subtle concern: When user is in "aprofundamento" mode and `evRenderInjuredPersonBoxes` is called (restoring), the function now calls evSyncDescaracterStageUi before the `evAprofundamentoOnlyMode` has been set? Wait, in the aprofundamento-only mode, they set evAprofundamentoOnlyMode = true (line 6530), then presumably render. Line 6538: "Sincroniza a UI de descaracterização agora que evAprofundamentoOnlyMode=true." So ordering there is fine. Also, could there be a problem that evSyncDescaracterStageUi at line 2709 in evCreateInjuredPersonCard runs BEFORE the card's fields/values were fully applied? In evCreateInjuredPersonCard, evApplyCardInjuryData(card, saved) already ran at line 2683, bind events, summary. Then after append and expand logic, sync runs. Fine. What about the call inside `evRenderInjuredPersonBoxes`: `evSelectPrimaryInjuredPerson`/`evEnsurePrimaryInjuredMarked` happen after loop (after card syncs). Do those change the suspect checkbox state? Possibly selectPrimary might change something? Unlikely to affect `.ev-inj-descaracter`. So final sync is just for safety. Is there any issue of duplicate registration: none, it's just function calls. Now let me check if there's any issue in `evCreateInjuredPersonCard` where `evSyncDescaracterStageUi` runs before the card is appended (wrap.appendChild at 2687 precedes 2709), so card exists in DOM when sync runs. Good — it queries `.ev-inj-descaracter` inside the card. Actually the sync queries all `.ev-inj-descaracter` globally, toggling `d-none` based on isAp. Since the new card was appended before the sync call, it will be toggled correctly. If it were appended after, the card would remain with whatever the template default had (d-none?) — well it is appended first. Good. One thing worth checking: The template's source `.ev-injured-person-box` may include `.ev-inj-descaracter` with `d-none` by default. When cloned, the sync removes d-none for AP type. That's exactly the fix. Good. Now let me confirm there are no other review files besides the two. The review group has two files: rule.json and _modal_event.html.twig. For rule.json, per rules: check spelling errors in JSON keys. The keys `path`, `merge_system_rule`, `rule` match. Content values ignored. So no issue in JSON. Actually wait: does the rule.json duplicate the entry? There were previously two entries referencing _modal_event.html.twig (see the context after the diff hunk includes another `"path": "src/Controller/SsmaController.php"`). Now there are two new entries: one for _modal_event.html.twig and one for _ev_injured_person_box.html.twig. But note the diff hunk shows existing entry for `templates/ssma/occurrence/partials/_modal_event.html.twig`?? Let me look at the full rule.json to see if _modal_event.html.twig was already mapped to another rule file (maybe action-plan-panel). If two rules map to the same path, that might be intentional (multiple rules). Let me read the file to verify. Actually the hunk header is `@@ -62,6 +62,16 @@` and the trailing context after the additions shows `"path": "src/Controller/SsmaController.php"`. Let me read rule.json fully around there to ensure keys not misspelled and structure is valid JSON (commas, braces). Let me read the file.
Tool Calls (1)
file_read
Show Details
{"file_path": ".opencodereview/rule.json"}
File: .opencodereview/rule.json (Total lines: 137)
IS_TRUNCATED: false
LINE_RANGE: 1-137
1|{
2|  "include": [
3|    "tests/**/*.php",
4|    "**/*.twig"
5|  ],
6|  "rules": [
7|    {
8|      "path": "src/Service/Interview/V2/**/*.php",
9|      "merge_system_rule": true,
10|      "rule": ".opencodereview/rules/interview-v2/service.md"
11|    },
12|    {
13|      "path": "src/Controller/Interview/V2/InterviewConversationV2Controller.php",
14|      "rule": ".opencodereview/rules/interview-v2/controller.md"
15|    },
16|    {
17|      "path": "src/Prompt/Interview/V2/Conversation/{ConversationSystemPrompt,MediaInteractionPrompt}.php",
18|      "rule": ".opencodereview/rules/interview-v2/prompt.md"
19|    },
20|    {
21|      "path": "tests/Unit/Product/PesquisaIaV2/**/*.php",
22|      "merge_system_rule": true,
23|      "rule": ".opencodereview/rules/interview-v2/tests.md"
24|    },
25|    {
26|      "path": "src/Service/ProjectCollaboratorAccessService.php",
27|      "merge_system_rule": true,
28|      "rule": ".opencodereview/rules/projects/task-deadline.md"
29|    },
30|    {
31|      "path": "src/Controller/ProjectsNewController.php",
32|      "merge_system_rule": true,
33|      "rule": ".opencodereview/rules/projects/task-deadline.md"
34|    },
35|    {
36|      "path": "public/js/projects/**/*.js",
37|      "merge_system_rule": true,
38|      "rule": ".opencodereview/rules/projects/task-deadline.md"
39|    },
40|    {
41|      "path": "templates/projects2.0/**/*.twig",
42|      "merge_system_rule": true,
43|      "rule": ".opencodereview/rules/projects/task-deadline.md"
44|    },
45|    {
46|      "path": "tests/Unit/Product/Projects/**/*.php",
47|      "merge_system_rule": true,
48|      "rule": ".opencodereview/rules/projects/task-deadline.md"
49|    },
50|    {
51|      "path": "src/Service/Ssma/ActionPlan/SsmaActionPlanPanel{Service,Presenter}.php",
52|      "merge_system_rule": true,
53|      "rule": ".opencodereview/rules/ssma/action-plan-panel.md"
54|    },
55|    {
56|      "path": "public/js/ssma/action_plan_panel.js",
57|      "merge_system_rule": true,
58|      "rule": ".opencodereview/rules/ssma/action-plan-panel.md"
59|    },
60|    {
61|      "path": "templates/ssma/action_plan/tabs/_tab_painel*.html.twig",
62|      "merge_system_rule": true,
63|      "rule": ".opencodereview/rules/ssma/action-plan-panel.md"
64|    },
65|    {
66|      "path": "templates/ssma/occurrence/partials/_modal_event.html.twig",
67|      "merge_system_rule": true,
68|      "rule": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md"
69|    },
70|    {
71|      "path": "templates/ssma/occurrence/partials/_ev_injured_person_box.html.twig",
72|      "merge_system_rule": true,
73|      "rule": ".opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md"
74|    },
75|    {
76|      "path": "src/Controller/SsmaController.php",
77|      "merge_system_rule": true,
78|      "rule": ".opencodereview/rules/ssma/occurrence-approve.md"
79|    },
80|    {
81|      "path": "templates/ssma/occurrence/occurrence_view.html.twig",
82|      "merge_system_rule": true,
83|      "rule": ".opencodereview/rules/ssma/occurrence-approve.md"
84|    },
85|    {
86|      "path": "src/Controller/**/*.php",
87|      "merge_system_rule": true,
88|      "rule": ".opencodereview/rules/backend/controller.md"
89|    },
90|    {
91|      "path": "src/Service/**/*.php",
92|      "merge_system_rule": true,
93|      "rule": ".opencodereview/rules/backend/service.md"
94|    },
95|    {
96|      "path": "src/Repository/**/*.php",
97|      "merge_system_rule": true,
98|      "rule": ".opencodereview/rules/backend/repository.md"
99|    },
100|    {
101|      "path": "src/{EventListener,EventSubscriber,Security/Voter}/**/*.php",
102|      "merge_system_rule": true,
103|      "rule": ".opencodereview/rules/backend/listener-voter.md"
104|    },
105|    {
106|      "path": "src/Command/**/*.php",
107|      "merge_system_rule": true,
108|      "rule": ".opencodereview/rules/backend/command.md"
109|    },
110|    {
111|      "path": "migrations/**/*.php",
112|      "merge_system_rule": true,
113|      "rule": ".opencodereview/rules/backend/migrations.md"
114|    },
115|    {
116|      "path": "src/**/*.php",
117|      "merge_system_rule": true,
118|      "rule": ".opencodereview/rules/backend/php-general.md"
119|    },
120|    {
121|      "path": "templates/**/*.twig",
122|      "merge_system_rule": true,
123|      "rule": ".opencodereview/rules/frontend/twig.md"
124|    },
125|    {
126|      "path": "public/js/**/*.js",
127|      "merge_system_rule": true,
128|      "rule": ".opencodereview/rules/frontend/js.md"
129|    },
130|    {
131|      "path": "tests/**/*.php",
132|      "merge_system_rule": true,
133|      "rule": ".opencodereview/rules/backend/tests.md"
134|    }
135|  ]
136|}
137|
Request #22 deepseek-v4-flash P:22.04K C:1.14K CR:20.22K CW:0 9050ms
Reasoning
The rule.json is valid JSON. Keys are consistent: `path`, `merge_system_rule`, `rule`. No spelling errors. Content values ignored per rules. One observation: `_modal_event.html.twig` now maps to `occurrence-descaracter-suspeita-card.md` rule. Before this diff, was `_modal_event.html.twig` already mapped? Let me check the diff context again: The hunk `@@ -62,6 +62,16 @@` and after additions the context line is `"path": "src/Controller/SsmaController.php"`. So before this change there was no rule for these two twig files at lines 62-76 — wait, the hunk shows the previous content at line 62-67 was the action-plan rule entry ending at line 64 (`"rule": ".opencodereview/rules/ssma/action-plan-panel.md"`), then line 65 `},`, then line 66 began `{` for SsmaController. So yes, new entries added. Now, the new entry for `_modal_event.html.twig` is a general rule mapping for OCR; since the review rule system is internal tooling, no functional issue. Wait — I notice in the review group the file `.opencodereview/rule.json` also references rule file `occurrence-descaracter-suspeita-card.md`, which is in the "other_changed_files"? Actually the user background lists `.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md` as changed file but it's not in the review group nor listed in other_changed_files. The `other_changed_files` block is empty. Anyway, rule file exists (found via search). Good. So JSON: no issues. Now the twig changes: two added function calls. Assess possible new bugs: Key observation: The rule doc itself instructs these calls, tests done. Function `evSyncDescaracterStageUi` is defined as a function declaration in the same script (hoisted), so calling before its textual definition is fine (2709 call textually comes before definition at 2917 — function declarations hoist, so no ReferenceError). Good. Now the redundancy aspect: line 2826 call in `evRenderInjuredPersonBoxes` after forEach where each iteration already called the sync at 2709. Minor. Not bug. Actually wait — is there a subtle issue where the sync in `evCreateInjuredPersonCard` runs for EACH card during the render loop, but during render, `evSelectedType()` may return the current type. For each card, isAp same. Fine. But hold on, is there a scenario where evCreateInjuredPersonCard is called BEFORE the user has selected type ACIDENTE_PESSOAL — e.g., when a draft is restored with type AP pre-selected? The type select value would be restored before render. In that case isAp true → blocks visible. If draft type select not yet restored when render called... evRestoreDraftIfAny restores type first? Data restore loop sets el.value for each key before render at end. So ev_person_type presumably restored. Fine. Now potential regression: In aprofundamento, when adding/editing an injured card via evCreateInjuredPersonCard, the call now runs evSyncDescaracterStageUi which toggles `is-doctor-aprof` class using evAprofundamentoOnlyMode. In aprofundamento mode it sets class → CSS makes Sim/Não visible. Same as before, since other call sites already did this. Edge: `evSyncDescaracterStageUi` unconditionally does `suspectWrap.classList.add('d-none')` for `#ev-suspeita-wrap`. If this function is called in a context where suspectWrap had been shown intentionally in creation step 1... but the comment states checkbox now per card, global remains hidden. Pre-existing behavior at other call sites. Fine. So the change appears functionally correct and matches documented rules. Potential real (minor) issue: Calling `evSyncDescaracterStageUi()` inside `evCreateInjuredPersonCard()` means N calls during `evRenderInjuredPersonBoxes()`, and the extra call at the end is redundant. But no real consequence. Per user-specific formatting rules, style-only findings should be short & low priority. Is redundancy worth a comment? It's arguably mild maintainability: the two added lines are fine; I think the confirmed finding already flagged the architecture issue. Let me think about whether there's a case where the sync at 2709 throws: evSelectedType() at 1553. Let's read it to make sure it doesn't throw when select not found. Also check whether evSyncDescaracterStageUi can be invoked before the DOM element `#form-event-new` exists (guarded). Let me read evSelectedType.
Tool Calls (1)
file_read
Show Details
{"end_line": 1565, "file_path": "templates/ssma/occurrence/partials/_modal_event.html.twig", "start_line": 1540}
File: templates/ssma/occurrence/partials/_modal_event.html.twig (Total lines: 7712)
IS_TRUNCATED: false
LINE_RANGE: 1540-1565
1540|    window.SSMA_EVENT_FORM_DEFAULTS = {{ ssma_event_form_defaults|default({})|json_encode|raw }};
1541|
1542|    var EV_KNOWN_TYPE_KEYS = ['ROS', 'QUASE_ACIDENTE', 'ACIDENTE_PESSOAL', 'ACIDENTE_MATERIAL', 'ACIDENTE_AMBIENTAL'];
1543|    /** Tipos com passo de Aprofundamento (ROS/QA + acidentes). */
1544|    var EV_APROFUNDAMENTO_TYPE_KEYS = ['ROS', 'QUASE_ACIDENTE', 'ACIDENTE_PESSOAL', 'ACIDENTE_MATERIAL', 'ACIDENTE_AMBIENTAL'];
1545|    /** Acidentes: bloco técnico (consequência real, lesão, etc.) dentro do Aprofundamento. */
1546|    var EV_APROFUNDAMENTO_ACCIDENT_KEYS = ['ACIDENTE_PESSOAL', 'ACIDENTE_MATERIAL', 'ACIDENTE_AMBIENTAL'];
1547|    var evCurrentStep = 'general';
1548|    var evAprofundamentoOnlyMode = false;
1549|    var evAprofundamentoFinalizeIntent = true;
1550|    var evAprofundamentoFinalized = false;
1551|    var evCorrectiveActionSeq = 0;
1552|
1553|    function evSelectedType() {
1554|        return (document.getElementById('ev_type') || { value: '' }).value || '';
1555|    }
1556|
1557|    /** ROS "Visto e resolvido" = SIM: dispensa o Aprofundamento Técnico (fluxo fica em 1 etapa). */
1558|    function evIsRosResolvedChecked() {
1559|        var chk = document.getElementById('ev_ros_resolved');
1560|        return !!(chk && chk.checked);
1561|    }
1562|
1563|    /** Reflete o estado do checkbox #ev_immediate_risk nos botões Sim/Não visíveis. */
1564|    function evSyncImmediateRiskButtonsUI() {
1565|        var riskChk = document.getElementById('ev_immediate_risk');
Request #23 deepseek-v4-flash P:23.74K C:1.93K CR:23.17K CW:0 14829ms
Reasoning
`evSelectedType` is robust. `evSyncDescaracterStageUi` is safe against missing elements. So the change is functionally benign and matches its purpose. Summary of review: - `.opencodereview/rule.json`: valid JSON, keys spelled consistently. No issue. Only change is mapping OCR rules to the affected templates. Fine. - Twig changes: two added calls to `evSyncDescaracterStageUi()`. These match the documented fix and are functionally correct: function is safe (guards), hoisted declaration, cards appended before sync, correct toggling. Should I mention the redundancy of the second call (evRenderInjuredPersonBoxes calls sync once per card via evCreateInjuredPersonCard plus once at the end)? Per the rules doc, removing them would be flagged as wrong; the doc explicitly says both should be called. So even if it's partially redundant, it's intentional & documented. Not a finding. I should not repeat the confirmed finding (#1 god template). Anything else? Let me consider XSS or security: none — no user data written; function calls only toggling CSS classes. Payload synchronization handled elsewhere, not modified. Edge-case regression check: The confirmed behavior difference: previously, when a card was created while type was NOT ACIDENTE_PESSOAL (e.g. user adds person before selecting type), the sync now will toggle `.ev-inj-descaracter` `d-none` true for those new cards. When the user then selects ACIDENTE_PESSOAL type, the change handler would run sync again, revealing. So consistent with rule "outros tipos → bloco oculto". Good. But wait — consider the creation flow order: user selects "ACIDENTE_PESSOAL" FIRST (per bug description "escolher tipo → adicionar pessoa → montar o card"). In evCreateInjuredPersonCard, previously after creating the card, since type is AP, the card's `.ev-inj-descaracter` with template default class `d-none`? Actually the rule doc says "#677 liberou o bloco na criação, mas o fluxo ... recriava o card com d-none sem novo sync". So the template's card box `.ev-inj-descaracter` has class `d-none` by default, and sync removes it. Adding sync at end of card creation fixes visibility. Now the second call added to evRenderInjuredPersonBoxes (line 2826) runs after evUpdateInjuredPersonsEmptyState; it would cover the case where the loop created cards... but cards already synced via create. Possibly the second call exists to also cover the "primary auto-created" card at line 2801-2805 (if primary added to details, then included in ids, so created via loop anyway). Both covered. Alternatively maybe the final call is for the scenario where evCreateInjuredPersonCard early-returns an existing card (line 2659) — but that only happens when not called from render (render clears innerHTML). In other flows like evAddInjuredPersonFromSelect — the add flow calls evCreateInjuredPersonCard when there's no existing. Fine. I think there are no further real issues to report. The only confirmed finding remains the architecture one. But maybe I should add one more useful comment: The call inside `evCreateInjuredPersonCard` makes the end-of-function call in `evRenderInjuredPersonBoxes` redundant... but docs explicitly require both; skip. Actually, wait — I should double check one thing: is the added call in `evCreateInjuredPersonCard` guaranteed to run after the card has been appended & expanded? Yes. And after `evEnsurePrimaryInjuredMarked()`. evEnsurePrimaryInjuredMarked could select a primary person, which might re-render or toggle classes? Possibly modifies card expansion. Not relevant to descaracter block. Let me consider a subtle detail regarding `evSyncDescaracterStageUi` and the `yesNoWrap` logic: it runs on each card and if suspChk.checked false, yesNoWrap gets `d-none`. In the creation, the checkbox is initially unchecked, so yesNoWrap hidden — correct. However there is one possible NEW bug: The call at line 2709 runs on EVERY newly created card. When a user checks "suspeita" on card A (checkbox) — does the checkbox change handler call evSyncDescaracterStageUi? Let's search for change listener of `.ev-inj-suspect-chk`. If it does, then Sim/Não would appear in creation once checked, contradicting rule 2 test "Marcar suspeita → Sim/Não não aparece na criação". But per CSS rule, in creation (`#form-event-new:not(.is-doctor-aprof)`) Sim/Não hidden regardless of `d-none` toggling. The CSS takes precedence (it's `#form-event-new:not(.is-doctor-aprof) .ev-inj-descaracter-toggle { display:none }` probably). Fine. Anyway, we are reviewing newly added lines; those only add sync calls. Given all this, my final verdict: No new issues besides the already confirmed one. I should call task_done. Wait, per instructions, I must confirm I've given every file in review_files its own pass: 1. `.opencodereview/rule.json` — checked JSON validity, key spelling consistent, no issue. Note: It's a config file for review rules; the change just registers rules for files; no bug. 2. `_modal_event.html.twig` — checked. Confirmed finding #1 already reported; no additional real issues. Should I also add the redundancy observation as a low-priority maintainability comment? Hmm. Actually let's reconsider: The rule doc explicitly says both calls required; the two lines are minimal. It is reasonable not to add noise. Actually, there might be a legitimately reportable concern: the added call in `evRenderInjuredPersonBoxes` at line 2826 calls `evSyncDescaracterStageUi()` right before the aprofundamento block that may `requestAnimationFrame` expand primary card. But that sync toggles visibility, so no conflict. Alright, let me finish. I'll provide a concise summary of the review in pt-BR and call task_done. Since confirmed finding already covers the only real issue, no new code_comment is needed. Wait — maybe I should still add a comment about the god template? No, it's a confirmed finding, must not repeat. OK. Let me summarize and finish.
Revisei os dois arquivos do grupo: **`.opencodereview/rule.json`** - As chaves novas (`path`, `merge_system_rule`, `rule`) seguem exatamente o padrão das entradas existentes, sem erros de grafia/estrutura. O JSON permanece válido (vírgulas e chaves corretas). As entradas apenas mapeiam regras de OCR para os templates afetados — sem impacto funcional no produto. **`templates/ssma/occurrence/partials/_modal_event.html.twig`** - Confirmei o contexto das duas chamadas adicionadas: - Linha 2709 (fim de `evCreateInjuredPersonCard`): o card já foi anexado ao DOM (`wrap.appendChild`) antes da chamada, e `evSyncDescaracterStageUi()` (declarada via function declaration, hoisted) é segura contra elementos ausentes (`evSelectedType`, `form-event-new`). Como o bloco `.ev-inj-descaracter` sai do template com `d-none`, o sync neste ponto é o que torna o checkbox visível — coerente com a regra documentada e com o bug descrito. - Linha 2826 (fim de `evRenderInjuredPersonBoxes`): roda após todos os cards serem recriados e após o tratamento do primário — cobre o caminho de restauração de draft. A chamada dentro de `evCreateInjuredPersonCard` já cobre cada card do loop, então esta última é parcialmente redundante, mas é o comportamento previsto pela própria regra do projeto (`.opencodereview/rules/ssma/occurrence-descaracter-suspeita-card.md`), não uma regressão. - Sem dados de usuário escritos no DOM, sem XSS, sem mudança de payload — só alternância de classes de visibilidade. Não identifiquei novos problemas funcionais além do já confirmado (orquestração de UI dentro do `<script>` gigante do template). Nenhum novo comentário é necessário; a única questão real (padrão "god template") já está registrada como achado confirmado.
Tool Calls (1)
task_done
Show Details
{"state": "DONE"}

review_filter_task

Request #1 deepseek-v4-flash P:2.86K C:620 CR:1.92K CW:0 5129ms
Reasoning
Let me analyze this comment. The comment c-0 is about the code in `templates/ssma/occurrence/partials/_modal_event.html.twig`. The comment says the fix is coherent with the described flow, but reinforces the pattern of UI orchestration within the giant `<script>` embedded in the template (file with ~7.7k lines mixing markup and screen logic). This makes unit testing, reuse, and maintenance difficult, and the project pattern dictates card/AJAX assembly logic goes to `public/js/`. It doesn't block this 2-line hotfix but registers tech debt to migrate `evSyncDescaracterStageUi` and card handlers to dedicated JS. Now, is this comment factually wrong per the diff? The comment targets code that IS in the diff — `evSyncDescaracterStageUi();` is added in two places in this file. The comment is about the architectural pattern: a large script embedded in the template, mixing markup and logic. Is the central claim contradicted by the diff? The comment describes the code as being in a giant `<script>` embedded in the template. The diff shows lines with JS code in a `.html.twig` file. We can see indented JS code being added in a template file, which is consistent with the comment's claim about embedded script logic. The comment isn't a factual claim that the diff contradicts. It's an opinion about code organization/maintainability. It states the code is inside a large script embedded in the template (~7,700 lines). We can't verify the file length from the diff, but the diff does show a template file with JS code. This isn't contradicted. Is this about style/readability? Partially. It's about the maintainability pattern of embedding JS in templates. That's not exactly protected category (not memory safety, concurrency, linkage, behavior change, unused param). Ground A: does the comment target code absent from the subject file's diff? No — the code `evSyncDescaracterStageUi();` is present in the diff (added in two places). The comment's description is about that code and the file containing it. Ground B: is there a diff line that literally contradicts its central claim? The central claim is that the fix adds to a giant embedded script pattern in the template. The diff shows the additions within a template file that has JS. Nothing contradicts it. Actually wait — is the comment true? The comment is somewhat subjective and advisory. It claims the project pattern sends card/AJAX assembly logic to `public/js/`. That's something we can't verify from the diff. But unverifiable isn't incorrect. So this comment should be approved. The comment is essentially about maintainability/tech debt — it's not factually wrong per the diff. Approve. I'll call approve_all_comments.
Tool Calls (1)
approve_all_comments
Show Details
{}