imeshuek commited on
Commit
09a448a
·
verified ·
1 Parent(s): ef8cb24

Upload vendor-contract-builder/page.tsx

Browse files
Files changed (1) hide show
  1. vendor-contract-builder/page.tsx +201 -0
vendor-contract-builder/page.tsx ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { useState } from 'react'
3
+
4
+ interface Deliverable { id:string; desc:string; due:string; qty:number; criteria:string }
5
+ interface Section { id:string; title:string; content:string; isDeliverables:boolean }
6
+
7
+ export default function VendorContractBuilder() {
8
+ const [clientName, setClientName] = useState('Amaya & Ruwan')
9
+ const [contractType, setContractType] = useState('Full Day Photography')
10
+ const [amount, setAmount] = useState('150000')
11
+ const [sections, setSections] = useState<Section[]>([
12
+ { id:'1', title:'Scope of Services', content:'The Photographer shall provide professional wedding photography services on the wedding date, including pre-ceremony and reception coverage.', isDeliverables:false },
13
+ { id:'2', title:'Deliverables', content:'', isDeliverables:true },
14
+ { id:'3', title:'Timeline & Schedule', content:'Booking confirmation within 5 days. Pre-wedding consultation 2 weeks before. Full gallery delivery within 30 days post-event.', isDeliverables:false },
15
+ { id:'4', title:'Payment Terms', content:`Total contract value: Rs. ${parseInt(amount).toLocaleString()}. Deposit of 33% due on signing. Balance due 7 days before event.`, isDeliverables:false },
16
+ { id:'5', title:'Cancellation Policy', content:'Cancellation >60 days before: deposit forfeited. Within 60 days: 50% due. Within 30 days: full amount due.', isDeliverables:false },
17
+ ])
18
+
19
+ const [deliverables, setDeliverables] = useState<Deliverable[]>([
20
+ { id:'d1', desc:'Edited high-resolution digital images', due:'30 days post-event', qty:400, criteria:'Professional quality edits, color-corrected' },
21
+ { id:'d2', desc:'Online gallery', due:'35 days post-event', qty:1, criteria:'Password protected, accessible for 12 months' },
22
+ { id:'d3', desc:'USB drive with full-resolution images', due:'40 days post-event', qty:1, criteria:'Branded USB, all RAW + edited files' },
23
+ { id:'d4', desc:'Premium layflat photo album', due:'60 days post-event', qty:1, criteria:'30 pages minimum, premium paper, leather cover' },
24
+ ])
25
+
26
+ const [selectedTemplate, setSelectedTemplate] = useState('photography')
27
+ const [showPreview, setShowPreview] = useState(false)
28
+
29
+ const templates = [
30
+ { id:'photography', name:'Photography Contract', desc:'Standard coverage, deliverables, and usage rights' },
31
+ { id:'venue', name:'Venue Contract', desc:'Ceremony & reception, guest capacity, amenities' },
32
+ { id:'catering', name:'Catering Contract', desc:'Menu selection, guest count, dietary accommodations' },
33
+ { id:'florist', name:'Floral Design Contract', desc:'Arrangements, seasonal blooms, setup & teardown' },
34
+ ]
35
+
36
+ const addSection = () => setSections([...sections,{id:Date.now().toString(),title:'New Section',content:'',isDeliverables:false}])
37
+ const addDeliverable = () => setDeliverables([...deliverables,{id:Date.now().toString(),desc:'New deliverable',due:'TBD',qty:1,criteria:''}])
38
+
39
+ return (
40
+ <div className="min-h-screen bg-ivory-50">
41
+ <div className="wedding-section py-8">
42
+ <div className="flex items-center justify-between mb-8">
43
+ <div>
44
+ <h1 className="font-heading text-3xl font-semibold text-charcoal-700">{showPreview?'Preview Contract':'Build Contract'}</h1>
45
+ <p className="text-charcoal-400 mt-1">Create a legally binding agreement with audit trail</p>
46
+ </div>
47
+ <div className="flex gap-3">
48
+ <select value={selectedTemplate} onChange={e=>setSelectedTemplate(e.target.value)}
49
+ className="wedding-input w-auto text-sm">
50
+ <option value="">Blank contract</option>
51
+ {templates.map(t=><option key={t.id} value={t.id}>{t.name}</option>)}
52
+ </select>
53
+ <button onClick={()=>setShowPreview(!showPreview)} className="wedding-btn-outline text-sm">
54
+ {showPreview?'← Edit':'Preview →'}
55
+ </button>
56
+ <button className="wedding-btn-gold text-sm">✉️ Send to Client</button>
57
+ </div>
58
+ </div>
59
+
60
+ {!showPreview ? (
61
+ <div className="grid lg:grid-cols-3 gap-6">
62
+ {/* Left: Contract builder */}
63
+ <div className="lg:col-span-2 space-y-6">
64
+ {/* Header */}
65
+ <div className="wedding-card p-6">
66
+ <div className="grid sm:grid-cols-2 gap-4">
67
+ <div>
68
+ <label className="text-xs font-semibold text-charcoal-500 uppercase tracking-wider mb-1 block">Client</label>
69
+ <input value={clientName} onChange={e=>setClientName(e.target.value)} className="wedding-input" />
70
+ </div>
71
+ <div>
72
+ <label className="text-xs font-semibold text-charcoal-500 uppercase tracking-wider mb-1 block">Service Type</label>
73
+ <input value={contractType} onChange={e=>setContractType(e.target.value)} className="wedding-input" />
74
+ </div>
75
+ <div>
76
+ <label className="text-xs font-semibold text-charcoal-500 uppercase tracking-wider mb-1 block">Amount (Rs)</label>
77
+ <input value={amount} onChange={e=>setAmount(e.target.value)} className="wedding-input" type="number" />
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ {/* Sections */}
83
+ {sections.map((sec,i) => (
84
+ <div key={sec.id} className="wedding-card p-6">
85
+ <div className="flex items-center gap-2 mb-4">
86
+ <span className="font-heading text-sm font-semibold text-gold-500">Section {i+1}</span>
87
+ <input value={sec.title} onChange={e=>{
88
+ const s=[...sections];s[i].title=e.target.value;setSections(s)
89
+ }} className="flex-1 text-lg font-heading font-semibold text-charcoal-700 bg-transparent border-none outline-none" />
90
+ <button className="text-rose-400 hover:text-rose-600 text-xs">Remove</button>
91
+ </div>
92
+ {!sec.isDeliverables ? (
93
+ <textarea value={sec.content} onChange={e=>{
94
+ const s=[...sections];s[i].content=e.target.value;setSections(s)
95
+ }} className="wedding-input h-24" placeholder="Enter contract text..." />
96
+ ) : (
97
+ <div className="space-y-4">
98
+ {/* Deliverables */}
99
+ {deliverables.map((d,j) => (
100
+ <div key={d.id} className="p-4 bg-ivory-50 rounded-xl border border-ivory-200">
101
+ <div className="grid sm:grid-cols-2 gap-3">
102
+ <div className="sm:col-span-2">
103
+ <label className="text-xs font-semibold text-charcoal-400 uppercase tracking-wider mb-1 block">Description</label>
104
+ <input value={d.desc} onChange={e=>{
105
+ const dl=[...deliverables];dl[j].desc=e.target.value;setDeliverables(dl)
106
+ }} className="wedding-input text-sm" />
107
+ </div>
108
+ <div>
109
+ <label className="text-xs font-semibold text-charcoal-400 uppercase tracking-wider mb-1 block">Due Date</label>
110
+ <input value={d.due} onChange={e=>{
111
+ const dl=[...deliverables];dl[j].due=e.target.value;setDeliverables(dl)
112
+ }} className="wedding-input text-sm" />
113
+ </div>
114
+ <div>
115
+ <label className="text-xs font-semibold text-charcoal-400 uppercase tracking-wider mb-1 block">Quantity</label>
116
+ <input value={d.qty} onChange={e=>{
117
+ const dl=[...deliverables];dl[j].qty=parseInt(e.target.value)||0;setDeliverables(dl)
118
+ }} className="wedding-input text-sm" type="number" />
119
+ </div>
120
+ <div className="sm:col-span-2">
121
+ <label className="text-xs font-semibold text-charcoal-400 uppercase tracking-wider mb-1 block">Acceptance Criteria</label>
122
+ <input value={d.criteria} onChange={e=>{
123
+ const dl=[...deliverables];dl[j].criteria=e.target.value;setDeliverables(dl)
124
+ }} className="wedding-input text-sm" />
125
+ </div>
126
+ </div>
127
+ <button className="text-xs text-rose-400 hover:text-rose-600 mt-2">Remove deliverable</button>
128
+ </div>
129
+ ))}
130
+ <button onClick={addDeliverable} className="wedding-btn-outline text-xs w-full">+ Add Deliverable</button>
131
+ </div>
132
+ )}
133
+ </div>
134
+ ))}
135
+ <button onClick={addSection} className="wedding-btn-outline text-sm w-full">+ Add Section</button>
136
+ </div>
137
+
138
+ {/* Right: Templates panel */}
139
+ <div className="space-y-6">
140
+ <div className="wedding-card p-6 sticky top-24">
141
+ <h2 className="font-heading text-lg font-semibold text-charcoal-700 mb-4">Templates</h2>
142
+ <p className="text-xs text-charcoal-400 mb-4">
143
+ Save and reuse contract structures per service category. Templates require admin approval before going live.
144
+ </p>
145
+ <div className="space-y-3">
146
+ {templates.map(t=>(
147
+ <button key={t.id} onClick={()=>setSelectedTemplate(t.id)}
148
+ className={"w-full text-left p-4 rounded-xl border transition-colors "+(selectedTemplate===t.id?'border-gold-300 bg-gold-50':'border-ivory-200 hover:border-gold-200')}>
149
+ <p className="font-heading text-sm font-semibold text-charcoal-700">{t.name}</p>
150
+ <p className="text-xs text-charcoal-400 mt-1">{t.desc}</p>
151
+ </button>
152
+ ))}
153
+ </div>
154
+ <div className="mt-4 pt-4 border-t border-ivory-200">
155
+ <button className="wedding-btn-gold w-full text-sm">💾 Save as Template</button>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ ) : (
161
+ /* Preview mode */
162
+ <div className="max-w-3xl mx-auto">
163
+ <div className="wedding-card p-10">
164
+ <div className="text-center mb-10 pb-10 border-b border-ivory-200">
165
+ <h1 className="font-heading text-3xl font-semibold text-charcoal-700 mb-2">{contractType}</h1>
166
+ <p className="text-sm text-charcoal-400">Between: Lens & Light Studio and {clientName}</p>
167
+ <p className="text-sm text-charcoal-400">Date: {new Date().toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'})}</p>
168
+ <p className="font-heading text-2xl font-semibold text-charcoal-700 mt-4">Rs. {parseInt(amount).toLocaleString()}</p>
169
+ </div>
170
+ <div className="space-y-8">
171
+ {sections.map((sec,i) => (
172
+ <div key={sec.id}>
173
+ <h3 className="font-heading text-lg font-semibold text-charcoal-700 mb-3">{i+1}. {sec.title}</h3>
174
+ {!sec.isDeliverables ? (
175
+ <p className="text-sm text-charcoal-500 leading-relaxed">{sec.content}</p>
176
+ ) : (
177
+ <div className="space-y-3">
178
+ {deliverables.map(d=>(
179
+ <div key={d.id} className="p-3 bg-ivory-50 rounded-lg">
180
+ <p className="text-sm font-medium text-charcoal-700">{d.desc}</p>
181
+ <p className="text-xs text-charcoal-400 mt-1">Due: {d.due} · Qty: {d.qty} · Criteria: {d.criteria}</p>
182
+ </div>
183
+ ))}
184
+ </div>
185
+ )}
186
+ </div>
187
+ ))}
188
+ </div>
189
+ <div className="mt-10 pt-10 border-t border-ivory-200">
190
+ <p className="text-xs text-charcoal-400 text-center">
191
+ This contract is created using the Evermore platform. All versions are immutably stored with a full audit trail.
192
+ E-signatures are legally binding under the Electronic Transactions Act.
193
+ </p>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ )}
198
+ </div>
199
+ </div>
200
+ )
201
+ }