직접 서명 기능 추가

This commit is contained in:
dohyeons
2025-10-02 10:04:02 +09:00
parent e697acb2c9
commit c52937c22d
2 changed files with 238 additions and 40 deletions

View File

@@ -11,6 +11,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Trash2, Settings, Database, Link2, Upload, Loader2 } from "lucide-react";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
import { QueryManager } from "./QueryManager";
import { SignaturePad } from "./SignaturePad";
import { reportApi } from "@/lib/api/reportApi";
import { useToast } from "@/hooks/use-toast";
@@ -636,48 +637,110 @@ export function ReportDesignerRightPanel() {
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{/* 파일 업로드 */}
<div>
<Label className="text-xs">
{selectedComponent.type === "signature" ? "서명 이미지" : "도장 이미지"}
</Label>
<div className="flex gap-2">
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleImageUpload}
className="hidden"
disabled={uploadingImage}
/>
<Button
type="button"
variant="outline"
size="sm"
onClick={() => fileInputRef.current?.click()}
disabled={uploadingImage}
className="flex-1"
>
{uploadingImage ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
<>
<Upload className="mr-2 h-4 w-4" />
{selectedComponent.imageUrl ? "파일 변경" : "파일 선택"}
</>
{/* 서명란: 탭으로 직접 서명 / 이미지 업로드 선택 */}
{selectedComponent.type === "signature" ? (
<Tabs defaultValue="draw" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="draw" className="text-xs">
</TabsTrigger>
<TabsTrigger value="upload" className="text-xs">
</TabsTrigger>
</TabsList>
{/* 직접 서명 탭 */}
<TabsContent value="draw" className="mt-3 space-y-2">
<SignaturePad
initialSignature={selectedComponent.imageUrl}
onSignatureChange={(dataUrl) => {
updateComponent(selectedComponent.id, {
imageUrl: dataUrl,
});
}}
/>
</TabsContent>
{/* 이미지 업로드 탭 */}
<TabsContent value="upload" className="mt-3 space-y-2">
<div className="flex gap-2">
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleImageUpload}
className="hidden"
disabled={uploadingImage}
/>
<Button
type="button"
variant="outline"
size="sm"
onClick={() => fileInputRef.current?.click()}
disabled={uploadingImage}
className="flex-1"
>
{uploadingImage ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
<>
<Upload className="mr-2 h-4 w-4" />
{selectedComponent.imageUrl ? "파일 변경" : "파일 선택"}
</>
)}
</Button>
</div>
<p className="text-xs text-gray-500">JPG, PNG, GIF, WEBP ( 10MB)</p>
{selectedComponent.imageUrl && !selectedComponent.imageUrl.startsWith("data:") && (
<p className="truncate text-xs text-indigo-600">: {selectedComponent.imageUrl}</p>
)}
</Button>
</TabsContent>
</Tabs>
) : (
// 도장란: 기존 방식 유지
<div>
<Label className="text-xs"> </Label>
<div className="flex gap-2">
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleImageUpload}
className="hidden"
disabled={uploadingImage}
/>
<Button
type="button"
variant="outline"
size="sm"
onClick={() => fileInputRef.current?.click()}
disabled={uploadingImage}
className="flex-1"
>
{uploadingImage ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
<>
<Upload className="mr-2 h-4 w-4" />
{selectedComponent.imageUrl ? "파일 변경" : "파일 선택"}
</>
)}
</Button>
</div>
<p className="mt-1 text-xs text-gray-500">JPG, PNG, GIF, WEBP ( 10MB)</p>
{selectedComponent.imageUrl && !selectedComponent.imageUrl.startsWith("data:") && (
<p className="mt-2 truncate text-xs text-indigo-600">
: {selectedComponent.imageUrl}
</p>
)}
</div>
<p className="mt-1 text-xs text-gray-500">JPG, PNG, GIF, WEBP ( 10MB)</p>
{selectedComponent.imageUrl && (
<p className="mt-2 truncate text-xs text-indigo-600">
: {selectedComponent.imageUrl}
</p>
)}
</div>
)}
{/* 맞춤 방식 */}
<div>