« Back to History
quality_add.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /* ========================================================= File: /erp/quality_add.php Purpose: Add Quality using Beams (warp) + single-row Weft Notes: - Ajax ?action=beam returns beam yarn rows - Weave type affects crimp (warp & weft) - Panna multiplies pano width - Weft [100m] auto: Denier & pano (inch) & peak (PPI) - Totals shown in grams & kg / 100 m - Removed Pasaria/Pissing/Sizing from UI ========================================================= */ error_reporting(E_ALL); ini_set('display_errors', 1); require __DIR__ . '/modules/auth/page_acl.php'; $ctx = page_require_access('quality_add'); $u = $ctx['user']; $company_id = (int)$ctx['company_id']; $pdo = isset($ctx['pdo']) && $ctx['pdo'] instanceof PDO ? $ctx['pdo'] : null; if(!$pdo){ require __DIR__ . '/core/db.php'; } /* helpers */ function pv($k,$d=null){ return isset($_POST[$k])? (is_array($_POST[$k])?$_POST[$k]:trim((string)$_POST[$k])) : $d; } function nvl($v){ return ($v==='' || $v===null) ? null : $v; } function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } /* auto code Q0001… per company */ function gen_quality_code(PDO $pdo, int $company_id): string { $st=$pdo->prepare("SELECT MAX(CAST(SUBSTRING(quality_code,2) AS UNSIGNED)) FROM qualities WHERE company_id=? AND quality_code REGEXP '^Q[0-9]+$'"); $st->execute([$company_id]); $n=(int)($st->fetchColumn()?:0); return 'Q'.str_pad((string)($n+1),4,'0',STR_PAD_LEFT); } /* ensure table (page-local; no global changes) */ $pdo->exec("CREATE TABLE IF NOT EXISTS qualities ( id BIGINT PRIMARY KEY AUTO_INCREMENT, company_id BIGINT NOT NULL, quality_name VARCHAR(191) NOT NULL, quality_code VARCHAR(100), category VARCHAR(100), loom_type VARCHAR(100), design_no VARCHAR(100), collection_name VARCHAR(150), epi DECIMAL(10,2), ppi DECIMAL(10,2), reed VARCHAR(50), pick VARCHAR(50), draft VARCHAR(100), denting VARCHAR(100), width_greige DECIMAL(10,2), width_finish DECIMAL(10,2), gsm DECIMAL(10,2), default_rate_m DECIMAL(10,2), peak_value DECIMAL(10,2), wastage_percent DECIMAL(10,2), unit_label VARCHAR(30) DEFAULT 'meter', notes TEXT, active TINYINT(1) DEFAULT 1, created_by BIGINT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, INDEX idx_company (company_id), UNIQUE KEY uniq_company_code (company_id, quality_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); /* -------- AJAX: fetch beam lines -------- */ if (($_GET['action'] ?? '') === 'beam') { header('Content-Type: application/json; charset=utf-8'); $bid = (int)($_GET['beam_id'] ?? 0); if ($bid<=0) { echo json_encode(['ok'=>false,'error'=>'invalid id']); exit; } try { $st = $pdo->prepare("SELECT id,name FROM beam_qualities WHERE id=:id AND company_id=:cid LIMIT 1"); $st->execute([':id'=>$bid, ':cid'=>$company_id]); $b = $st->fetch(PDO::FETCH_ASSOC); if (!$b) { echo json_encode(['ok'=>false,'error'=>'not found']); exit; } $it = $pdo->prepare("SELECT seq_no,stock_type,yarn_type,denier,color,total_tar,tpm,weight,wastage FROM beam_quality_yarns WHERE beam_quality_id=:bid ORDER BY seq_no ASC, id ASC"); $it->execute([':bid'=>$bid]); $rows = $it->fetchAll(PDO::FETCH_ASSOC) ?: []; echo json_encode(['ok'=>true,'beam'=>$b,'items'=>$rows], JSON_UNESCAPED_UNICODE); } catch(Throwable $e){ echo json_encode(['ok'=>false,'error'=>$e->getMessage()]); } exit; } /* -------- dropdowns -------- */ $beam_options = []; try{ $st = $pdo->prepare("SELECT id,name FROM beam_qualities WHERE company_id=? ORDER BY id DESC"); $st->execute([$company_id]); $beam_options = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; }catch(Throwable $e){} $weft_deniers = []; try{ $st = $pdo->prepare("SELECT DISTINCT denier FROM yarn_company_data WHERE company_id=? AND denier IS NOT NULL AND denier<>'' ORDER BY denier"); $st->execute([$company_id]); $weft_deniers = array_map(fn($r)=>$r['denier'], $st->fetchAll(PDO::FETCH_ASSOC)); }catch(Throwable $e){} /* -------- save -------- */ $success = $error = null; if ($_SERVER['REQUEST_METHOD']==='POST') { $quality_name = pv('quality_name',''); if ($quality_name==='') { $error = 'Quality Name required.'; } $quality_code = pv('quality_code', null); $category = pv('quality_group', pv('category', null)); $loom_type = pv('select_machine', pv('loom_type', null)); $design_no = pv('design_no', null); $collection_name = pv('collection_name', null); $machine_cut = pv('machine_cut', null); $after_finish_cut= pv('after_finish_cut', null); $epi = pv('epi', null); $ppi = pv('ppi', null); $reed = pv('reed', null); $pick = pv('pick', null); $draft = pv('draft', null); $denting = pv('denting', null); $width_greige = pv('width_greige', null); $width_finish = pv('width_finish', null); $gsm = pv('gsm', null); $production_rate_m = pv('production_rate_m', null); // label kept generic $peak_value = pv('peak_value', null); $wastage_percent = pv('wastage_percent', null); $unit_label = pv('unit_label', 'meter'); $notes = pv('notes', null); /* stash extras (no schema changes) */ $extras = [ 'primary_beam_id' => (int)pv('primary_beam_id',0), 'secondary_beam_id' => (int)pv('secondary_beam_id',0), // snapshot of warp rows 'tana_yarn_type' => pv('tana_yarn_type',[]), 'tana_denier' => pv('tana_denier',[]), 'tana_color' => pv('tana_color',[]), 'tana_taar' => pv('tana_taar',[]), 'tana_tpm' => pv('tana_tpm',[]), 'tana_weight100' => pv('tana_weight100',[]), 'tana_wastage' => pv('tana_wastage',[]), // weft single row 'weft_denier' => pv('weft_denier',''), 'weft_pano' => pv('weft_pano',''), 'weft_peak' => pv('weft_peak',''), 'weft_weight100' => pv('weft_weight100',''), 'weft_wastage' => pv('weft_wastage',''), // production toggles 'prod_type' => pv('prod_type','taka'), 'beam_type' => pv('beam_type_store','double'), 'weave_type' => pv('weave_type_store','dobby'), 'panna' => pv('panna_store','1'), 'machine_cut' => $machine_cut, 'after_finish_cut' => $after_finish_cut, ]; $notes_final = trim((string)$notes); $notes_final .= ($notes_final ? "\n\n" : "") . "__EXTRAS__\n" . json_encode($extras, JSON_UNESCAPED_UNICODE); if ($quality_code === null || $quality_code === '') { $quality_code = gen_quality_code($pdo, $company_id); } if (!$error) { try{ $sql="INSERT INTO qualities (company_id, quality_name, quality_code, category, loom_type, design_no, collection_name, epi, ppi, reed, pick, draft, denting, width_greige, width_finish, gsm, default_rate_m, peak_value, wastage_percent, unit_label, notes, active, created_by) VALUES (:company_id,:quality_name,:quality_code,:category,:loom_type,:design_no,:collection_name, :epi,:ppi,:reed,:pick,:draft,:denting, :width_greige,:width_finish,:gsm, :default_rate_m,:peak_value,:wastage_percent,:unit_label,:notes,1,:created_by)"; $st=$pdo->prepare($sql); $st->execute([ ':company_id'=>$company_id, ':quality_name'=>$quality_name, ':quality_code'=>nvl($quality_code), ':category'=>nvl($category), ':loom_type'=>nvl($loom_type), ':design_no'=>nvl($design_no), ':collection_name'=>nvl($collection_name), ':epi'=>nvl($epi), ':ppi'=>nvl($ppi), ':reed'=>nvl($reed), ':pick'=>nvl($pick), ':draft'=>nvl($draft), ':denting'=>nvl($denting), ':width_greige'=>nvl($width_greige), ':width_finish'=>nvl($width_finish), ':gsm'=>nvl($gsm), ':default_rate_m'=>nvl($production_rate_m), ':peak_value'=>nvl($peak_value), ':wastage_percent'=>nvl($wastage_percent), ':unit_label'=>($unit_label===''?'meter':$unit_label), ':notes'=>$notes_final, ':created_by'=>(int)$u['id'], ]); $success="Quality saved."; } catch(Throwable $e){ $error="Save failed: ".h($e->getMessage()); } } } ?> <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Add Quality • Beams + Weft</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> :root{ --primary:#34A853; --bg:#F7FFFA; --text:#202124; --shadow:0 10px 24px rgba(0,0,0,.08); --radius:14px; } *{box-sizing:border-box} body{margin:0;background:var(--bg);font-family:Inter,system-ui,Segoe UI,Roboto,Arial,sans-serif;color:var(--text)} .wrap{max-width:1180px;margin:24px auto;padding:0 16px} .card{background:#fff;border-radius:var(--radius);box-shadow:var(--shadow);padding:16px;margin-bottom:16px} h2,h3{margin:0 0 10px} .grid{display:grid;gap:12px} .g-2{grid-template-columns:repeat(2,minmax(0,1fr))} .g-3{grid-template-columns:repeat(3,minmax(0,1fr))} label{font-size:12px;color:#5f6368;margin-bottom:4px;display:block} input,select,textarea{ width:100%;padding:9px 10px;border:1px solid #e5e7eb;border-radius:10px } table{width:100%;border-collapse:collapse;margin-top:8px} th,td{border:1px solid #e5e7eb;padding:6px 8px;font-size:13px} .row-flex{display:flex;gap:10px;align-items:center;flex-wrap:wrap} .note{font-size:12px;color:#2e7d32} .warn{font-size:12px;color:#b00020} .hide{display:none} .pill{display:inline-block;background:#eaf6ee;border-radius:999px;padding:6px 10px;font-size:12px;color:#1e8e3e;margin-top:6px} </style> </head> <body> <?php require __DIR__ . '/partials/header.php'; ?> <div class="wrap"> <div class="card"> <h2>Beam Source</h2> <?php if($success): ?><div class="pill"><?= h($success) ?></div><?php endif; ?> <?php if($error): ?><div class="warn" style="background:#fde7ea;padding:10px;border-radius:10px"><?= h($error) ?></div><?php endif; ?> <div class="row-flex" style="gap:24px;margin-bottom:8px"> <div> <label>Beam Type *</label> <div class="row-flex"> <label><input type="radio" name="beam_type_sel" value="single"> Single</label> <label><input type="radio" name="beam_type_sel" value="double" checked> Double</label> </div> <div class="pill">Beam type toggles second beam selector.</div> </div> </div> <div class="grid g-2"> <div> <label>Primary Beam</label> <select id="primary_beam" onchange="onBeamChange(this.value,false)"> <option value="">— Select beam —</option> <?php foreach($beam_options as $o): ?> <option value="<?= (int)$o['id'] ?>"><?= h($o['name']) ?></option> <?php endforeach; ?> </select> </div> <div id="secBeamWrap"> <label>Secondary Beam (append all rows)</label> <select id="secondary_beam" onchange="onBeamChange(this.value,true)"> <option value="">— Select beam —</option> <?php foreach($beam_options as $o): ?> <option value="<?= (int)$o['id'] ?>"><?= h($o['name']) ?></option> <?php endforeach; ?> </select> </div> </div> <!-- hidden ids to save --> <input type="hidden" id="primary_beam_id" name="primary_beam_id"> <input type="hidden" id="secondary_beam_id" name="secondary_beam_id"> <input type="hidden" id="beam_type_store" name="beam_type_store" value="double"> <input type="hidden" id="weave_type_store" name="weave_type_store" value="dobby"> <input type="hidden" id="panna_store" name="panna_store" value="1"> </div> <div class="card"> <h2>Quality Details</h2> <form method="post" id="qForm" autocomplete="off"> <div class="grid g-3"> <div><label>Quality/Design Name *</label><input name="quality_name" required></div> <div><label>Quality Group</label> <select name="quality_group"><option>SAREE</option><option>DRESS</option><option>OTHER</option></select> </div> <div><label>Quality Code</label><input name="quality_code" placeholder="Leave blank to auto-generate"></div> <div><label>Select Machine</label> <select name="select_machine"> <option>Looms</option><option>Rapier</option><option>Jacquard</option><option>Dobby</option> </select> </div> <div><label>Machine Cut</label><input name="machine_cut" type="number" step="0.01" value="6.14"></div> <div><label>After Finish Cut</label><input name="after_finish_cut" type="number" step="0.01" value="6.05"></div> <div><label>EPI</label><input name="epi" type="number" step="0.01"></div> <div><label>PPI</label><input name="ppi" type="number" step="0.01"></div> <div><label>Reed</label><input name="reed"></div> <div><label>Draft</label><input name="draft"></div> <div><label>Denting</label><input name="denting"></div> </div> <div class="pill">Use weave type + panna below to affect crimp and weft width.</div> <h3 style="margin-top:8px;">Production Details</h3> <div class="grid g-3"> <div> <label>Production Type *</label> <div class="row-flex"> <label><input type="radio" name="prod_type" value="taka" checked> Taka (In house)</label> <label><input type="radio" name="prod_type" value="purchase"> Purchase/Trading</label> <label><input type="radio" name="prod_type" value="job"> Job</label> </div> </div> <div> <label>Weave Type *</label> <div class="row-flex"> <label><input type="radio" name="weave_type" value="plain"> Plain</label> <label><input type="radio" name="weave_type" value="twill"> Twill</label> <label><input type="radio" name="weave_type" value="satin"> Satin</label> <label><input type="radio" name="weave_type" value="dobby" checked> Dobby</label> </div> </div> <div> <label>Panna *</label> <div class="row-flex"> <label><input type="radio" name="panna" value="1" checked> Single</label> <label><input type="radio" name="panna" value="2"> Double</label> <label><input type="radio" name="panna" value="3"> Triple</label> <label><input type="radio" name="panna" value="4"> Four</label> </div> </div> </div> <h3 style="margin-top:8px;">Warping Detail (Tana) — from Beams</h3> <table> <thead> <tr><th>#</th><th>Yarn Type</th><th>Denier</th><th>Color</th><th>Total Taar</th><th>TPM</th><th>Weight [100M] (g)</th><th>Wastage %</th></tr> </thead> <tbody id="tanaBody"></tbody> </table> <div class="note" id="warpTot">Warp Total = 0.000 g (0.000 kg) per 100 m</div> <h3 style="margin-top:14px;">Weft Detail (Wana)</h3> <table> <thead> <tr><th>Denier/Count</th><th>Pano (inch)</th><th>Peak (PPI)</th><th>TPM</th><th>Weight [100M] (g)</th><th>Wastage %</th></tr> </thead> <tbody> <tr> <td> <select name="weft_denier" id="weft_denier"> <option value="">— Select —</option> <?php foreach($weft_deniers as $d): ?> <option value="<?= h($d) ?>"><?= h($d) ?></option> <?php endforeach; ?> </select> </td> <td><input id="weft_pano" name="weft_pano" type="number" step="0.01" placeholder="inch"></td> <td><input id="weft_peak" name="weft_peak" type="number" step="0.1" placeholder="PPI"></td> <td><input id="weft_tpm_dummy" type="number" step="1" placeholder="(optional)"></td> <td><input id="weft_weight100" name="weft_weight100" type="number" step="0.001" readonly></td> <td><input id="weft_wastage" name="weft_wastage" type="number" step="0.01" value="5"></td> </tr> </tbody> </table> <div class="note" id="weftInfo">Width/Picks info will appear here.</div> <div class="note" id="weftTot">Weft Total = 0.000 g (0.000 kg) per 100 m</div> <div class="pill" id="grandTot">Grand Total (Warp + Weft) = 0.000 g (0.000 kg) per 100 m</div> <h3 style="margin-top:12px;">Other</h3> <div class="grid g-3"> <div><label>Production Rate (per meter)</label><input name="production_rate_m" type="number" step="0.01"></div> <div><label>Peak Value</label><input name="peak_value" type="number" step="0.01"></div> <div><label>Wastage %</label><input name="wastage_percent" type="number" step="0.01"></div> <div><label>GSM</label><input name="gsm" type="number" step="0.01"></div> <div><label>Width (Greige)</label><input name="width_greige" type="number" step="0.01"></div> <div><label>Width (Finish)</label><input name="width_finish" type="number" step="0.01"></div> <div><label>Unit Label</label><input name="unit_label" value="meter"></div> </div> <h3 style="margin-top:12px;">Notes</h3> <textarea name="notes" placeholder="Any remarks..."></textarea> <div style="display:flex;gap:10px;margin-top:14px"> <button type="submit" class="btn btn-primary" style="background:#34A853;color:#fff;border:0;border-radius:10px;padding:10px 14px">Save Quality</button> </div> </form> </div> </div> <script> /* ================= helpers ================= */ const $ = (q,ctx=document)=>ctx.querySelector(q); const $$ = (q,ctx=document)=>Array.from(ctx.querySelectorAll(q)); const tanaBody = $('#tanaBody'); const secBeamWrap= $('#secBeamWrap'); function esc(v){ return (v==null?'':String(v)).replace(/"/g,'"'); } function parseDenier(str){ if(!str) return 0; const s = String(str).trim(); if (s.includes('/')) { const left = s.split('/',1)[0].trim(); const m = left.match(/\d+(\.\d+)?/); return m ? parseFloat(m[0]) : 0; } const m = s.match(/\d+(\.\d+)?/); return m ? parseFloat(m[0]) : 0; } /* ====== weave/panna first (so currentCrimp safe) ====== */ const crimpMap = { plain:{warp:0.02,weft:0.055}, twill:{warp:0.02,weft:0.04}, satin:{warp:0.01,weft:0.02}, dobby:{warp:0.02,weft:0.05} }; const weaveRadios = $$('input[name="weave_type"]'); function currentCrimp(){ const w = (weaveRadios.find(r=>r.checked)||{}).value || 'dobby'; $('#weave_type_store').value = w; return crimpMap[w] || crimpMap['dobby']; } weaveRadios.forEach(r=>r.addEventListener('change', ()=>recalcTotals())); const pannaRadios = $$('input[name="panna"]'); function currentPanna(){ const p = parseInt((pannaRadios.find(r=>r.checked)||{}).value || '1',10); $('#panna_store').value = String(p); return Math.max(1,Math.min(4,p)); } pannaRadios.forEach(r=>r.addEventListener('change', ()=>recalcTotals())); /* =========== totals + weft auto weight =========== */ function recalcWeftAuto(){ const den = parseDenier($('#weft_denier').value); const pano_in = parseFloat($('#weft_pano').value)||0; const ppi = parseFloat($('#weft_peak').value)||0; const panna = currentPanna(); const cr = currentCrimp().weft; const width_total_in = pano_in * panna; // total width per shot (inch) const width_m = width_total_in * 0.0254; // inch → meter const picks_100m = ppi * 3937.007874; // inches in 100 m const length_m = width_m * picks_100m * (1 + cr); // Denier grams/9000m const grams = (den/9000) * length_m; $('#weft_weight100').value = isFinite(grams) ? grams.toFixed(3) : ''; const info = `Width from Pano: ${width_total_in.toFixed(2)} inch = ${width_m.toFixed(3)} m | Weft crimp ${(cr*100).toFixed(1)}% Picks @100m: ${Math.round(picks_100m).toLocaleString()} | Raw Weft Weight: ${grams.toFixed(3)} g`; $('#weftInfo').textContent = info; } function recalcTotals(){ // Warp grams with wastage + crimp let warp_g=0; const crw = currentCrimp().warp; $$('#tanaBody tr').forEach(tr=>{ const w = parseFloat(tr.querySelector('input[name="tana_weight100[]"]').value)||0; const wz = parseFloat(tr.querySelector('input[name="tana_wastage[]"]').value)||0; warp_g += w * (1 + wz/100); }); warp_g *= (1 + crw); $('#warpTot').textContent = `Warp Total = ${warp_g.toFixed(3)} g (${(warp_g/1000).toFixed(3)} kg) per 100 m`; // Weft auto + wastage recalcWeftAuto(); const ww = parseFloat($('#weft_weight100').value)||0; const wws = parseFloat($('#weft_wastage').value)||0; const weft_g = ww * (1 + wws/100); $('#weftTot').textContent = `Weft Total = ${weft_g.toFixed(3)} g (${(weft_g/1000).toFixed(3)} kg) per 100 m`; const grand_g = warp_g + weft_g; $('#grandTot').textContent = `Grand Total (Warp + Weft) = ${grand_g.toFixed(3)} g (${(grand_g/1000).toFixed(3)} kg) per 100 m`; } ['weft_denier','weft_pano','weft_peak','weft_wastage'].forEach(id=>{ const el = document.getElementById(id); if(el){ el.addEventListener('input', recalcTotals); el.addEventListener('change', recalcTotals); } }); /* =========== beam type toggle (single/double) =========== */ const beamTypeRadios = $$('input[name="beam_type_sel"]'); function syncBeamType(){ const val = (beamTypeRadios.find(r=>r.checked)||{}).value || 'double'; $('#beam_type_store').value = val; if(val==='single'){ secBeamWrap.classList.add('hide'); $('#secondary_beam').value=''; $('#secondary_beam_id').value=''; }else{ secBeamWrap.classList.remove('hide'); } recalcTotals(); } beamTypeRadios.forEach(r=>r.addEventListener('change', syncBeamType)); /* =========== beam fetch/build =========== */ async function getBeam(id){ try{ const url = `${location.pathname}?action=beam&beam_id=${encodeURIComponent(id)}`; const r = await fetch(url, { credentials: 'same-origin' }); if(!r.ok){ alert('Beam fetch failed ('+r.status+')'); return null; } const j = await r.json().catch(()=>null); if(!j || !j.ok){ alert('Beam fetch failed: ' + (j && j.error ? j.error : 'bad JSON')); return null; } return j; }catch(e){ alert('Network error: ' + (e?.message || e)); return null; } } function buildTana(items, append){ items = Array.isArray(items)?items:[]; if(!append){ tanaBody.innerHTML=''; } items.forEach((r)=>{ const tr = document.createElement('tr'); tr.innerHTML = ` <td>${($$('tr', tanaBody).length+1)}</td> <td><input name="tana_yarn_type[]" value="${esc(r.yarn_type)}"></td> <td><input name="tana_denier[]" value="${esc(r.denier)}"></td> <td><input name="tana_color[]" value="${esc(r.color)}"></td> <td><input name="tana_taar[]" type="number" step="1" value="${isFinite(+r.total_tar)?+r.total_tar:''}"></td> <td><input name="tana_tpm[]" type="number" step="1" value="${isFinite(+r.tpm)?+r.tpm:''}"></td> <td><input name="tana_weight100[]" type="number" step="0.001" value="${isFinite(+r.weight)?+r.weight:''}" oninput="recalcTotals()"></td> <td><input name="tana_wastage[]" type="number" step="0.01" value="${isFinite(+r.wastage)?+r.wastage:0}" oninput="recalcTotals()"></td> `; tanaBody.appendChild(tr); }); recalcTotals(); } // global for inline onchange window.onBeamChange = async function(id, append){ (append ? $('#secondary_beam_id') : $('#primary_beam_id')).value = id || ''; if(!id){ if(!append) tanaBody.innerHTML=''; recalcTotals(); return; } const j = await getBeam(id); if(j) buildTana(j.items, append); }; // attach also via addEventListener for safety window.addEventListener('DOMContentLoaded', ()=>{ const pb = $('#primary_beam'), sb = $('#secondary_beam'); if(pb) pb.addEventListener('change', e=>window.onBeamChange(e.target.value,false)); if(sb) sb.addEventListener('change', e=>window.onBeamChange(e.target.value,true)); }); /* =========== bootstrap =========== */ syncBeamType(); recalcTotals(); window.addEventListener('load', async () => { const pid = $('#primary_beam')?.value; if (pid) { const j = await getBeam(pid); if (j) buildTana(j.items, false); } const isDouble = (document.querySelector('input[name="beam_type_sel"]:checked')?.value || 'double')==='double'; const sid = $('#secondary_beam')?.value; if (isDouble && sid) { const j2 = await getBeam(sid); if (j2) buildTana(j2.items, true); } }); // keep hidden ids on submit $('#qForm').addEventListener('submit', ()=>{ $('#primary_beam_id').value = $('#primary_beam').value || ''; $('#secondary_beam_id').value = $('#secondary_beam').value || ''; $('#beam_type_store').value = (document.querySelector('input[name="beam_type_sel"]:checked')?.value || 'double'); $('#weave_type_store').value = (weaveRadios.find(r=>r.checked)||{}).value || 'dobby'; $('#panna_store').value = String(currentPanna()); }); </script> </body> </html>