summaryrefslogtreecommitdiff
path: root/lib/bidding/bidding-notice-editor.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/bidding-notice-editor.tsx')
-rw-r--r--lib/bidding/bidding-notice-editor.tsx230
1 files changed, 230 insertions, 0 deletions
diff --git a/lib/bidding/bidding-notice-editor.tsx b/lib/bidding/bidding-notice-editor.tsx
new file mode 100644
index 00000000..03b993b9
--- /dev/null
+++ b/lib/bidding/bidding-notice-editor.tsx
@@ -0,0 +1,230 @@
+'use client'
+
+import { useState, useTransition } from 'react'
+import { useRouter } from 'next/navigation'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import { useToast } from '@/hooks/use-toast'
+import { Save, RefreshCw } from 'lucide-react'
+import { BiddingNoticeTemplate } from '@/db/schema/bidding'
+import { saveBiddingNoticeTemplate } from './service'
+import TiptapEditor from '@/components/qna/tiptap-editor'
+
+interface BiddingNoticeEditorProps {
+ initialData: BiddingNoticeTemplate | null
+}
+
+export function BiddingNoticeEditor({ initialData }: BiddingNoticeEditorProps) {
+ const [title, setTitle] = useState(initialData?.title || '표준 입찰공고문')
+ const [content, setContent] = useState(initialData?.content || getDefaultTemplate())
+ const [isPending, startTransition] = useTransition()
+ const { toast } = useToast()
+ const router = useRouter()
+
+ const handleSave = () => {
+ if (!title.trim()) {
+ toast({
+ title: '오류',
+ description: '제목을 입력해주세요.',
+ variant: 'destructive',
+ })
+ return
+ }
+
+ if (!content.trim()) {
+ toast({
+ title: '오류',
+ description: '내용을 입력해주세요.',
+ variant: 'destructive',
+ })
+ return
+ }
+
+ startTransition(async () => {
+ try {
+ await saveBiddingNoticeTemplate({ title, content })
+ toast({
+ title: '성공',
+ description: '입찰공고문 템플릿이 저장되었습니다.',
+ })
+ router.refresh()
+ } catch (error) {
+ toast({
+ title: '오류',
+ description: error instanceof Error ? error.message : '저장에 실패했습니다.',
+ variant: 'destructive',
+ })
+ }
+ })
+ }
+
+ const handleReset = () => {
+ if (confirm('기본 템플릿으로 초기화하시겠습니까? 현재 내용은 삭제됩니다.')) {
+ setTitle('표준 입찰공고문')
+ setContent(getDefaultTemplate())
+ toast({
+ title: '초기화 완료',
+ description: '기본 템플릿으로 초기화되었습니다.',
+ })
+ }
+ }
+
+ return (
+ <div className="space-y-6">
+ {/* 제목 입력 */}
+ <div className="space-y-2">
+ <Label htmlFor="title">템플릿 제목</Label>
+ <Input
+ id="title"
+ value={title}
+ onChange={(e) => setTitle(e.target.value)}
+ placeholder="입찰공고문 제목을 입력하세요"
+ disabled={isPending}
+ />
+ </div>
+
+ {/* 에디터 */}
+ <div className="space-y-2">
+ <Label>공고문 내용</Label>
+ <div className="border rounded-lg">
+ <TiptapEditor
+ content={content}
+ setContent={setContent}
+ disabled={isPending}
+ height="500px"
+ />
+ </div>
+ </div>
+
+ {/* 액션 버튼 */}
+ <div className="flex items-center gap-4 pt-4">
+ <Button
+ onClick={handleSave}
+ disabled={isPending}
+ className="min-w-[120px]"
+ >
+ {isPending ? (
+ <>
+ <RefreshCw className="w-4 h-4 mr-2 animate-spin" />
+ 저장 중...
+ </>
+ ) : (
+ <>
+ <Save className="w-4 h-4 mr-2" />
+ 저장
+ </>
+ )}
+ </Button>
+
+ <Button
+ variant="outline"
+ onClick={handleReset}
+ disabled={isPending}
+ >
+ 기본 템플릿으로 초기화
+ </Button>
+
+ {initialData && (
+ <div className="ml-auto text-sm text-muted-foreground">
+ 마지막 업데이트: {new Date(initialData.updatedAt).toLocaleString('ko-KR')}
+ </div>
+ )}
+ </div>
+
+ {/* 미리보기 힌트 */}
+ <div className="bg-muted/50 p-4 rounded-lg">
+ <p className="text-sm text-muted-foreground">
+ <strong>💡 사용 팁:</strong>
+ 이 템플릿은 실제 입찰 공고 작성 시 기본값으로 사용됩니다.
+ 회사 정보, 표준 조건, 서식 등을 미리 작성해두면 편리합니다.
+ </p>
+ </div>
+ </div>
+ )
+}
+
+// 기본 템플릿 함수
+function getDefaultTemplate(): string {
+ return `
+<h1>입찰공고</h1>
+
+<h2>1. 입찰 개요</h2>
+<ul>
+ <li><strong>공고명:</strong> [입찰 공고명을 입력하세요]</li>
+ <li><strong>입찰방식:</strong> [일반경쟁입찰/제한경쟁입찰]</li>
+ <li><strong>입찰공고번호:</strong> [공고번호]</li>
+ <li><strong>공고일자:</strong> [YYYY년 MM월 DD일]</li>
+</ul>
+
+<h2>2. 입찰 참가자격</h2>
+<ul>
+ <li>관련 업종의 사업자등록증을 보유한 업체</li>
+ <li>부가가치세법에 의한 사업자등록증을 보유한 업체</li>
+ <li>기타 관련 법령에 따른 자격 요건을 갖춘 업체</li>
+</ul>
+
+<h2>3. 입찰 일정</h2>
+<table>
+ <thead>
+ <tr>
+ <th>구분</th>
+ <th>일시</th>
+ <th>장소</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>입찰 공고</td>
+ <td>[YYYY.MM.DD]</td>
+ <td>-</td>
+ </tr>
+ <tr>
+ <td>현장설명</td>
+ <td>[YYYY.MM.DD HH:MM]</td>
+ <td>[현장 주소]</td>
+ </tr>
+ <tr>
+ <td>입찰서 접수</td>
+ <td>[YYYY.MM.DD HH:MM까지]</td>
+ <td>[접수 장소]</td>
+ </tr>
+ <tr>
+ <td>개찰</td>
+ <td>[YYYY.MM.DD HH:MM]</td>
+ <td>[개찰 장소]</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2>4. 입찰 대상</h2>
+<ul>
+ <li><strong>사업명:</strong> [사업명을 입력하세요]</li>
+ <li><strong>사업내용:</strong> [상세 사업내용]</li>
+ <li><strong>사업기간:</strong> [계약일로부터 OO일 이내]</li>
+ <li><strong>사업장소:</strong> [사업 수행 장소]</li>
+</ul>
+
+<h2>5. 제출 서류</h2>
+<ul>
+ <li>입찰서 및 투찰서</li>
+ <li>사업자등록증 사본</li>
+ <li>법인등기부등본 (법인의 경우)</li>
+ <li>업체현황서</li>
+ <li>기타 입찰 참가자격 증명서류</li>
+</ul>
+
+<h2>6. 기타 사항</h2>
+<ul>
+ <li>본 입찰공고에 명시되지 않은 사항은 관련 법령에 따릅니다.</li>
+ <li>기타 문의사항은 아래 연락처로 문의하시기 바랍니다.</li>
+</ul>
+
+<blockquote>
+<p><strong>문의처:</strong><br>
+담당자: [담당자명]<br>
+전화: [전화번호]<br>
+이메일: [이메일주소]</p>
+</blockquote>
+ `.trim()
+} \ No newline at end of file