Some checks failed
tests / ci (pull_request) Has been cancelled
- Implemented CourseRegistrationShow component for admin to view registration details. - Added course registration creation and listing for students. - Enhanced student registration edit and create forms with improved error handling. - Introduced new columns for course registration submissions in student view. - Updated routes for course registration management in admin and student contexts. - Added logging and status handling for course registration submissions. - Improved type definitions for course registration and user roles.
98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
import { Eraser } from 'lucide-react';
|
|
import { useRef } from 'react';
|
|
import SignatureCanvasImport from 'react-signature-canvas';
|
|
import InputError from '@/components/input-error';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Label } from '@/components/ui/label';
|
|
|
|
// react-signature-canvas ships as a CommonJS/UMD bundle. Vite's dev-server
|
|
// pre-bundling wraps its `module.exports` (itself `{ __esModule: true,
|
|
// default: SignatureCanvas }`) as the ESM default export, so the default
|
|
// import resolves one layer too shallow. Unwrap it before use.
|
|
const SignatureCanvas = ((
|
|
SignatureCanvasImport as unknown as {
|
|
default?: typeof SignatureCanvasImport;
|
|
}
|
|
).default ?? SignatureCanvasImport) as typeof SignatureCanvasImport;
|
|
|
|
type SignaturePadProps = {
|
|
name: string;
|
|
error?: string;
|
|
};
|
|
|
|
export function SignaturePad({ name, error }: SignaturePadProps) {
|
|
const padRef = useRef<SignatureCanvasImport>(null);
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
function syncFileInput() {
|
|
const pad = padRef.current;
|
|
const input = fileInputRef.current;
|
|
|
|
if (!pad || !input || pad.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
pad.getTrimmedCanvas().toBlob((blob: Blob | null) => {
|
|
if (!blob) {
|
|
return;
|
|
}
|
|
|
|
const file = new File([blob], 'signature.png', {
|
|
type: 'image/png',
|
|
});
|
|
const dataTransfer = new DataTransfer();
|
|
dataTransfer.items.add(file);
|
|
input.files = dataTransfer.files;
|
|
}, 'image/png');
|
|
}
|
|
|
|
function handleClear() {
|
|
padRef.current?.clear();
|
|
|
|
if (fileInputRef.current) {
|
|
fileInputRef.current.value = '';
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="grid gap-2">
|
|
<div className="flex items-center justify-between">
|
|
<Label>
|
|
Tanda Tangan <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleClear}
|
|
>
|
|
<Eraser className="h-4 w-4" />
|
|
Hapus
|
|
</Button>
|
|
</div>
|
|
<div className="overflow-hidden rounded-md border bg-white">
|
|
<SignatureCanvas
|
|
ref={padRef}
|
|
penColor="#0f172a"
|
|
canvasProps={{
|
|
className:
|
|
'h-[180px] w-full cursor-crosshair touch-none',
|
|
}}
|
|
onEnd={syncFileInput}
|
|
/>
|
|
</div>
|
|
<input
|
|
ref={fileInputRef}
|
|
type="file"
|
|
name={name}
|
|
className="hidden"
|
|
/>
|
|
<p className="text-xs text-muted-foreground">
|
|
Gambar tanda tangan Anda pada kotak di atas menggunakan mouse
|
|
atau layar sentuh.
|
|
</p>
|
|
<InputError message={error} />
|
|
</div>
|
|
);
|
|
}
|