PageSourceSearch

https://construction-app-lime-six.vercel.app/assets/PtwDetail-IWo7ugHn.js

js construction-app-lime-six.vercel.app collected 2026-10-03 06:34:02 UTC 15,512 bytes, 12 lines download raw bytes

1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index-C1c_T_mp.js","./viewer-pdf-CxNKkqmB.js","./viewer-pdf-Bu5q60D-.css"])))=>i.map(i=>d[i]);
2import{r as l,j as e}from"./viewer-pdf-CxNKkqmB.js";import{i as V,u as se,j as ae,k as re,l as ie,s as U,m as ne,X as le,n as $,M as H,h as B,g as X,o as ce,A as M,p as oe,P as W,q as Z,a as K,D as de,R as xe,C as G,U as me}from"./index-Dyq-TIMv.js";import{P as Y,S as ue}from"./SignatureProofCard-C97wwjhY.js";import{u as J,a as ee,O as Q,P as he,i as pe,e as fe,h as je,r as ge}from"./OfflineBanner-MWJAqS0n.js";import{_ as be}from"./reports-pdf-a_ADGKMy.js";import{p as _e}from"./publicVerify-CV2si9KV.js";import{m as Ne}from"./ptw-jwt-bYNj2IUv.js";import{exportComplianceProofPack as ve}from"./export-B_V0f5ya.js";import"./reports-xlsx-CkbGwgij.js";import"./index-IyFhxked.js";/**
3 * @license lucide-react v0.363.0 - ISC
4 *
5 * This source code is licensed under the ISC license.
6 * See the LICENSE file in the root directory of this source tree.
7 */const ye=V("ArrowLeftToLine",[["path",{d:"M3 19V5",key:"rwsyhb"}],["path",{d:"m13 6-6 6 6 6",key:"1yhaz7"}],["path",{d:"M7 12h14",key:"uoisry"}]]);/**
8 * @license lucide-react v0.363.0 - ISC
9 *
10 * This source code is licensed under the ISC license.
11 * See the LICENSE file in the root directory of this source tree.
12 */const we=V("Flame",[["path",{d:"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z",key:"96xj49"}]]);function Se({ptw:r,onAction:u}){const{profile:d}=se(),{projects:C,memberships:z}=ae(),{approve:L,requestRevision:I,reject:O,adminOverride:q}=J(),{requireStepUp:A}=re(),{requireSignReauth:T}=ie(),_=ee(),[x,c]=l.useState(null),[t,g]=l.useState(""),[a,o]=l.useState(null),[y,f]=l.useState(!1),w=(d==null?void 0:d.global_role)==="admin",N=Array.isArray(r.chain_snapshot)?r.chain_snapshot:null,b=(N==null?void 0:N[r.current_step])??null,v=b==null?void 0:b.required_role,j=(b==null?void 0:b.optional_user_id)??null,[S,k]=l.useState([]);l.useEffect(()=>{if(!d){k([]);return}let n=!1;const m=new Date().toISOString().slice(0,10);return U.from("delegations").select("user_id").eq("delegate_to",d.id).lte("valid_from",m).gte("valid_until",m).then(({data:h,error:p})=>{if(!n){if(p){console.error("delegations fetch error:",p),k([]);return}k((h??[]).map(D=>D.user_id))}}),()=>{n=!0}},[d]);const P=l.useMemo(()=>{if(!d||r.status!=="in_review")return!1;if(w)return!0;if(!v)return!1;if(j)return j===d.id;const n=C.find(h=>h.id===r.project_id),m=h=>v==="pm"&&((n==null?void 0:n.assigned_pm_ids.includes(h))??!1)||z.some(p=>p.project_id===r.project_id&&p.user_id===h&&p.status==="approved"&&p.role===v);return m(d.id)?!0:S.some(m)},[d,w,v,j,C,z,r.project_id,r.status,S]);if(r.status!=="in_review"||!P&&!w)return null;async function E(n){if(await A("approval")){f(!0),o(null);try{const{error:m}=await L(r.id);if(m)throw new Error(m);if(!await T()){f(!1);return}const{error:h}=await U.rpc("record_ptw_signoff",{p_ptw_id:r.id,p_signature_b64:n});if(h)throw new Error(h.message);c(null),u()}catch(m){o(m instanceof Error?m.message:"簽核失敗")}finally{f(!1)}}}async function R(){if(t.trim().length<10){o("需要至少 10 個字元嘅原因");return}if(await A("approval")){f(!0),o(null);try{let n;if(x==="revision")n=await I(r.id,t);else if(x==="reject")n=await O(r.id,t);else if(x==="admin_override")n=await q(r.id,t);else return;if(n.error)throw new Error(n.error);g(""),c(null),u()}catch(n){o(n instanceof Error?n.message:"操作失敗")}finally{f(!1)}}}return e.jsxs(e.Fragment,{children:[!_&&e.jsx("div",{className:"mb-2",children:e.jsx(Q,{})}),e.jsxs("div",{className:"card p-3 flex flex-wrap gap-2 sticky bottom-0",children:[P&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{type:"button",className:"btn-primary flex-1 min-w-[8rem]",disabled:!_,onClick:()=>c("sign"),children:[e.jsx(ne,{size:16,className:"inline mr-1"}),"簽署批准"]}),e.jsxs("button",{type:"button",className:"btn-ghost flex-1 min-w-[7rem] text-amber-700",disabled:!_,onClick:()=>c("revision"),children:[e.jsx(ye,{size:16,className:"inline mr-1"}),"退回"]}),e.jsxs("button",{type:"button",className:"btn-ghost flex-1 min-w-[6rem] text-red-700",disabled:!_,onClick:()=>c("reject"),children:[e.jsx(le,{size:16,className:"inline mr-1"}),"拒絕"]})]}),w&&v!=="safety_officer"&&e.jsxs("button",{type:"button",className:"btn-ghost flex-1 min-w-[8rem] text-purple-700",disabled:!_,onClick:()=>c("admin_override"),children:[e.jsx($,{size:16,className:"inline mr-1"}),"管理員指派"]})]}),x==="sign"&&e.jsxs(H,{open:!0,title:"簽署批准",onClose:()=>c(null),children:[e.jsx(Y,{title:"簽名以批准呢張工作許可證",onSign:E,onCancel:()=>c(null)}),y&&e.jsx("p",{className:"mt-2 text-sm text-site-500",children:"處理中..."}),a&&e.jsx("p",{className:"mt-2 text-sm text-red-600",children:a})]}),(x==="revision"||x==="reject"||x==="admin_override")&&e.jsx(H,{open:!0,title:x==="revision"?"退回":x==="reject"?"拒絕":"管理員指派",onClose:()=>{c(null),g(""),o(null)},children:e.jsxs("div",{className:"space-y-3",children:[e.jsx("label",{className:"label",children:"原因 (最少 10 個字元)"}),e.jsx("textarea",{className:"input",rows:4,value:t,onChange:n=>
12g(n.target.value),placeholder:"說明原因..."}),e.jsxs("p",{className:"text-xs text-site-500 text-right",children:[t.length,"/10+"]}),a&&e.jsx("p",{className:"text-sm text-red-600",children:a}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("button",{type:"button",className:"btn-ghost flex-1",onClick:()=>{c(null),g(""),o(null)},children:"取消"}),e.jsx("button",{type:"button",className:"btn-primary flex-1",onClick:R,disabled:y||t.trim().length<10,children:y?"處理中...":"確認"})]})]})})]})}const ke=l.lazy(()=>be(()=>import("./index-C1c_T_mp.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(r=>({default:r.QRCodeSVG})));function Pe({token:r,error:u}){return e.jsxs("div",{className:"card p-4 space-y-2",children:[e.jsx("h3",{className:"text-base font-semibold text-site-900",children:"驗證 QR"}),u?e.jsx("p",{className:"text-sm text-red-600",children:u}):r?e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(l.Suspense,{fallback:e.jsx(B,{size:32}),children:e.jsx("div",{className:"bg-white p-3 rounded-xl border border-site-200",children:e.jsx(ke,{value:_e(r),size:208,level:"M",includeMargin:!0})})}),e.jsx("p",{className:"text-xs text-site-500 text-center max-w-xs",children:"任何人用手機掃描即可驗證此許可證真偽及有效期(無需登入)。每次掃描會寫入 permit_scans 審計紀錄。"})]}):e.jsx(B,{size:16})]})}function Ce(){var p,D;const{id:r,ptwId:u}=X(),d=ce(),{ptws:C,versionsByPtw:z,workersByPtw:L,approvalsByPtw:I,signoffsByPtw:O,scansByPtw:q,loading:A,startFireWatch:T,closeOut:_,refetch:x}=J(),c=ee(),t=l.useMemo(()=>C.find(s=>s.id===u),[C,u]),g=l.useMemo(()=>{if(!t)return null;const s=z[t.id]||[];return s.find(i=>i.id===t.current_version_id)||s[s.length-1]||null},[t,z]),a=g==null?void 0:g.payload,o=t?pe(t):!1,y=t?fe(t):null,[f,w]=l.useState(null),[N,b]=l.useState(null),[v,j]=l.useState(!1),[S,k]=l.useState(-1);if(l.useEffect(()=>{(t==null?void 0:t.status)==="active"&&!o&&f===null&&!N&&Ne(t.id).then(({token:s,error:i})=>{i?b(i):w(s)})},[t==null?void 0:t.id,t==null?void 0:t.status,o,f,N]),l.useEffect(()=>{if(!t||t.ptw_type!=="hot_work")return;if(!t.fire_watch_started_at){k(-1);return}const s=()=>k(ge(t));s();const i=window.setInterval(s,1e3);return()=>window.clearInterval(i)},[t]),A)return e.jsx(M,{title:"工作許可證",children:e.jsx("div",{className:"py-12 text-center",children:e.jsx(B,{size:32})})});if(!t)return e.jsx(M,{title:"工作許可證",children:e.jsx("div",{className:"card p-8 text-center text-sm text-site-500",children:"找不到工作許可證"})});const P=L[t.id]||[],E=I[t.id]||[],R=O[t.id]||[],n=q[t.id]||[];async function m(){if(!t)return;const{error:s}=await T(t.id);s?alert(s):x()}async function h(s){if(!t)return;const{error:i}=await _(t.id,s);i?alert(i):(j(!1),x())}return e.jsxs(M,{title:t.number,children:[e.jsxs("div",{className:"space-y-4 pb-24",children:[e.jsxs("button",{type:"button",onClick:()=>d(`/project/${r}/ptw`),className:"text-sm text-site-600 inline-flex items-center",children:[e.jsx(oe,{size:16,className:"inline"}),"返回列表"]}),e.jsxs("div",{className:"card p-4 space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("h2",{className:"text-2xl font-bold text-site-900",children:t.number}),e.jsx("span",{className:"px-3 py-1 rounded-full text-sm font-medium "+(o?"bg-red-50 text-red-600":"bg-site-100 text-site-700"),children:y?W[y]:W[t.status]})]}),e.jsxs("p",{className:"text-xs font-mono text-site-400",children:["DWSS: ",Z("ptw",parseInt(((p=t.number.match(/\d+/))==null?void 0:p[0])??"0",10))]}),e.jsx("p",{className:"text-sm text-site-600",children:K[t.ptw_type]}),e.jsxs("button",{type:"button",onClick:()=>{var s;return ve({docKindZh:"工作許可證 (PTW)",docNumber:t.number,dwssRefStr:Z("ptw",parseInt(((s=t.number.match(/\d+/))==null?void 0:s[0])??"0",10)),statusZh:W[t.status],detailZh:K[t.ptw_type],chainRolesZh:Array.isArray(t.chain_snapshot)?t.chain_snapshot.map(i=>xe[i==null?void 0:i.required_role]??(i==null?void 0:i.required_role)??"—"):void 0})},className:"btn-ghost w-full mt-1 inline-flex items-center justify-center gap-1.5 text-xs",children:[e.jsx(de,{size:14})," 匯出合規證明 (PDF)"]}),t.status==="active"&&t.expires_at&&e.jsxs("p",{className:"text-sm flex items-center gap-1 "+(o?"text-red-600":"text-site-600"),children:[e.jsx(G,{size:14}),e.jsxs("span",{children:[o?"已過期:":"有效至 ",new Date(t.expires_at).toLocaleStr
12ing("zh-HK")," 香港時間"]})]}),o&&e.jsx("div",{className:"rounded-xl bg-red-50 border border-red-200 p-3 text-sm text-red-700",children:"此許可證已過期,二維碼已失效,不可再憑此證施工。請重新申請。"})]}),a&&e.jsxs("div",{className:"card p-4 space-y-3",children:[e.jsx("h3",{className:"text-base font-semibold text-site-900",children:"工作內容"}),e.jsx("p",{className:"whitespace-pre-wrap text-sm text-site-700",children:a.description}),((D=a.checklist)==null?void 0:D.length)>0&&e.jsxs(e.Fragment,{children:[e.jsx("h4",{className:"text-sm font-semibold text-site-900 mt-2",children:"安全核對"}),e.jsx("ul",{className:"text-sm space-y-1",children:a.checklist.map(s=>e.jsxs("li",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"inline-flex h-5 w-5 items-center justify-center rounded-full text-xs "+(s.value===!0?"bg-green-500 text-white":"bg-site-200 text-site-500"),children:s.value===!0?"✓":"–"}),e.jsx("span",{className:s.required?"text-site-900":"text-site-600",children:s.label_zh})]},s.key))})]}),(a.valid_from||a.valid_to)&&e.jsxs(e.Fragment,{children:[e.jsx("h4",{className:"text-sm font-semibold text-site-900 mt-2",children:"有效時段"}),e.jsxs("p",{className:"text-sm text-site-700 flex items-center gap-1",children:[e.jsx(G,{size:14}),e.jsxs("span",{children:[a.valid_from?new Date(a.valid_from).toLocaleString("zh-HK"):"—"," 至 ",a.valid_to?new Date(a.valid_to).toLocaleString("zh-HK"):"—"]})]})]}),a.gas_test&&(a.gas_test.o2||a.gas_test.h2s||a.gas_test.co||a.gas_test.lel)&&e.jsxs(e.Fragment,{children:[e.jsx("h4",{className:"text-sm font-semibold text-site-900 mt-2",children:"氣體測試"}),e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-4 gap-2 text-sm",children:[e.jsx(F,{label:"O₂",value:a.gas_test.o2,unit:"%"}),e.jsx(F,{label:"H₂S",value:a.gas_test.h2s,unit:"ppm"}),e.jsx(F,{label:"CO",value:a.gas_test.co,unit:"ppm"}),e.jsx(F,{label:"LEL",value:a.gas_test.lel,unit:"%"})]})]}),a.hazards&&a.hazards.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("h4",{className:"text-sm font-semibold text-site-900 mt-2",children:"已識別危害"}),e.jsx("ul",{className:"text-sm space-y-1 list-disc pl-5 text-site-700",children:a.hazards.map((s,i)=>e.jsx("li",{children:s},i))})]}),a.controls&&a.controls.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("h4",{className:"text-sm font-semibold text-site-900 mt-2",children:"控制措施"}),e.jsx("ul",{className:"text-sm space-y-1 list-disc pl-5 text-site-700",children:a.controls.map((s,i)=>e.jsx("li",{children:s},i))})]})]}),P.length>0&&e.jsxs("div",{className:"card p-4 space-y-2",children:[e.jsxs("h3",{className:"text-base font-semibold text-site-900 flex items-center gap-2",children:[e.jsx(me,{size:16}),"工人名單 (",P.length,")"]}),e.jsx("ul",{className:"text-sm space-y-1",children:P.map(s=>e.jsxs("li",{className:"text-site-700",children:[s.worker_name,s.worker_phone&&e.jsxs("span",{className:"text-site-500 ml-2",children:["(",s.worker_phone,")"]})]},s.id))})]}),t.status==="active"&&!o&&e.jsx(Pe,{token:f,error:N}),t.status==="active"&&t.ptw_type==="hot_work"&&e.jsxs("div",{className:"card p-4 space-y-2",children:[e.jsxs("h3",{className:"text-base font-semibold text-site-900 flex items-center gap-2",children:[e.jsx(we,{size:16,className:"text-red-500"}),"火警監察"]}),t.fire_watch_started_at?S>0?e.jsxs("p",{className:"text-sm text-amber-700",children:["還需 ",Math.floor(S/60)," 分 ",S%60," 秒"]}):e.jsx("p",{className:"text-sm text-green-700",children:"火警監察已完成 — 可以關閉"}):e.jsx("button",{type:"button",className:"btn-ghost",disabled:!c,onClick:m,children:"開始 30 分鐘火警監察"}),je(t)&&e.jsx("button",{type:"button",className:"btn-primary mt-2",disabled:!c,onClick:()=>j(!0),children:"關閉許可證"}),!c&&e.jsx(Q,{})]}),t.status==="active"&&t.ptw_type!=="hot_work"&&e.jsxs(e.Fragment,{children:[e.jsx("button",{type:"button",className:"btn-primary",disabled:!c,onClick:()=>j(!0),children:"關閉許可證"}),!c&&e.jsx(Q,{})]}),E.length>0&&e.jsxs("div",{className:"card p-4 space-y-2",children:[e.jsxs("h3",{className:"text-base font-semibold text-site-900 flex items-center gap-2",children:[e.jsx($,{size:16}
12),"簽核紀錄 (",E.length,")"]}),e.jsx("ul",{className:"text-sm space-y-1",children:E.slice().sort((s,i)=>Date.parse(s.created_at)-Date.parse(i.created_at)).map(s=>{const i=R.find(te=>te.approval_id===s.id);return e.jsxs("li",{className:"text-site-700",children:["Step ",s.step_order+1,": ",s.action_type,e.jsx("span",{className:"text-site-500 ml-2",children:new Date(s.created_at).toLocaleString("zh-HK")}),i&&e.jsx("span",{className:"text-green-700 ml-2",children:"✓ 簽名"})]},s.id)})})]}),R.length>0&&e.jsx("div",{className:"space-y-3",children:R.slice().sort((s,i)=>Date.parse(s.created_at)-Date.parse(i.created_at)).map(s=>e.jsx(ue,{kind:"ptw",signoffId:s.id},s.id))}),n.length>0&&e.jsxs("div",{className:"card p-4",children:[e.jsxs("h3",{className:"text-base font-semibold text-site-900",children:["巡查紀錄 (",n.length,")"]}),e.jsxs("p",{className:"text-sm text-site-500",children:["最近: ",new Date(n[0].scanned_at).toLocaleString("zh-HK")]})]}),e.jsx(Se,{ptw:t,onAction:()=>x()})]}),e.jsx(H,{open:v,title:"關閉工作許可證",onClose:()=>j(!1),children:e.jsx(Y,{title:"簽名以確認完工關閉",onSign:h,onCancel:()=>j(!1)})})]})}function F({label:r,value:u,unit:d}){return e.jsxs("div",{className:"rounded-xl border border-site-200 bg-site-50 px-3 py-2 text-center",children:[e.jsx("div",{className:"text-xs text-site-500",children:r}),e.jsx("div",{className:"font-semibold text-site-900",children:u?`${u} ${d}`:"—"})]})}function Te(){const{id:r}=X();return r?e.jsx(he,{projectId:r,children:e.jsx(Ce,{})}):null}export{Te as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.