feat(06-03): signing page — server component, PDF viewer, field overlays, progress bar
- page.tsx: server component validates JWT + one-time-use before rendering any UI - Three error states (expired/used/invalid) show static pages with no canvas - SigningPageClientWrapper: dynamic import (ssr:false) for react-pdf browser requirement - SigningPageClient: full-scroll PDF viewer with pulsing blue field overlays - Field overlay coordinates convert PDF user-space (bottom-left) to screen (top-left) - SigningProgressBar: sticky bottom bar with X/Y count + jump-to-next + submit button - api/sign/[token]/pdf: token-authenticated PDF streaming route (no agent auth)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
'use client';
|
||||
|
||||
interface SigningProgressBarProps {
|
||||
total: number;
|
||||
signed: number;
|
||||
onJumpToNext: () => void;
|
||||
onSubmit: () => void;
|
||||
submitting: boolean;
|
||||
}
|
||||
|
||||
export function SigningProgressBar({
|
||||
total,
|
||||
signed,
|
||||
onJumpToNext,
|
||||
onSubmit,
|
||||
submitting,
|
||||
}: SigningProgressBarProps) {
|
||||
const allSigned = signed >= total;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 50,
|
||||
backgroundColor: '#1B2B4B',
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '12px 24px',
|
||||
boxShadow: '0 -2px 8px rgba(0,0,0,0.15)',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '15px' }}>
|
||||
{signed} of {total} signature{total !== 1 ? 's' : ''} complete
|
||||
</span>
|
||||
<div style={{ display: 'flex', gap: '12px' }}>
|
||||
{!allSigned && (
|
||||
<button
|
||||
onClick={onJumpToNext}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
border: '1px solid #C9A84C',
|
||||
color: '#C9A84C',
|
||||
padding: '8px 18px',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
Jump to Next
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={onSubmit}
|
||||
disabled={!allSigned || submitting}
|
||||
style={{
|
||||
backgroundColor: allSigned ? '#C9A84C' : '#555',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
padding: '8px 22px',
|
||||
borderRadius: '4px',
|
||||
cursor: allSigned ? 'pointer' : 'not-allowed',
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
opacity: submitting ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{submitting ? 'Submitting...' : 'Submit Signature'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user