Skip to content

Commit 88884db

Browse files
authored
Add files via upload
0 parents  commit 88884db

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

‎index.html‎

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Art Proofing Portal</title>
7+
<!-- Load Tailwind CSS for styling -->
8+
<script src="https://cdn.tailwindcss.com"></script>
9+
</head>
10+
<body class="bg-gray-100 text-gray-900 font-sans min-h-screen p-4 md:p-8">
11+
12+
<div id="app" class="max-w-4xl mx-auto">
13+
<!-- The JavaScript below will inject either the Admin Dashboard or Customer View here -->
14+
</div>
15+
16+
<!-- Load Firebase Modular SDKs as an ES module -->
17+
<script type="module">
18+
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js";
19+
import { getFirestore, collection, addDoc, onSnapshot, doc, updateDoc, getDoc } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
20+
21+
// 1. REPLACE WITH YOUR FIREBASE CONFIG
22+
const firebaseConfig = {
23+
apiKey: "YOUR_API_KEY",
24+
authDomain: "YOUR_AUTH_DOMAIN",
25+
projectId: "YOUR_PROJECT_ID",
26+
storageBucket: "YOUR_STORAGE_BUCKET",
27+
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
28+
appId: "YOUR_APP_ID"
29+
};
30+
31+
const app = initializeApp(firebaseConfig);
32+
const db = getFirestore(app);
33+
34+
// 2. Routing logic: Check if the URL has a '?id=...' parameter
35+
const urlParams = new URLSearchParams(window.location.search);
36+
const proofId = urlParams.get('id');
37+
const appDiv = document.getElementById('app');
38+
39+
if (proofId) {
40+
renderCustomerView(proofId);
41+
} else {
42+
renderAdminView();
43+
}
44+
45+
// --- CUSTOMER VIEW ---
46+
async function renderCustomerView(id) {
47+
appDiv.innerHTML = `<p class="text-xl font-bold animate-pulse">Loading your proof...</p>`;
48+
try {
49+
const docRef = doc(db, "proofs", id);
50+
const docSnap = await getDoc(docRef);
51+
52+
if (docSnap.exists()) {
53+
const data = docSnap.data();
54+
55+
let statusColor = "text-yellow-600";
56+
if (data.status === "Approved") statusColor = "text-green-600";
57+
if (data.status === "Denied") statusColor = "text-red-600";
58+
59+
appDiv.innerHTML = `
60+
<div class="bg-white p-6 md:p-10 rounded-xl shadow-lg border border-gray-200">
61+
<h1 class="text-3xl font-black uppercase tracking-tight mb-2">Review Your Design</h1>
62+
<p class="mb-6 text-gray-500">Client: <span class="font-bold text-gray-900">${data.customerName}</span></p>
63+
64+
<img src="${data.imageUrl}" alt="Art Proof" class="w-full max-w-2xl border-4 border-gray-100 rounded shadow-sm mb-6">
65+
66+
<div class="bg-gray-50 p-4 rounded border mb-6">
67+
<p class="font-bold text-sm uppercase text-gray-500">Current Status</p>
68+
<p class="text-2xl font-black uppercase ${statusColor}">${data.status}</p>
69+
</div>
70+
71+
${data.status === 'Pending' ? `
72+
<div class="flex flex-wrap gap-4">
73+
<button id="approveBtn" class="bg-green-600 text-white px-8 py-4 rounded font-bold uppercase tracking-wide hover:bg-green-700 transition-colors">Approve Design</button>
74+
<button id="denyBtn" class="bg-red-600 text-white px-8 py-4 rounded font-bold uppercase tracking-wide hover:bg-red-700 transition-colors">Request Changes</button>
75+
</div>
76+
` : `
77+
<p class="text-gray-600 italic">This proof has been closed.</p>
78+
`}
79+
</div>
80+
`;
81+
82+
if (data.status === 'Pending') {
83+
document.getElementById('approveBtn').addEventListener('click', () => updateStatus(id, 'Approved'));
84+
document.getElementById('denyBtn').addEventListener('click', () => updateStatus(id, 'Denied'));
85+
}
86+
} else {
87+
appDiv.innerHTML = `<h1 class="text-2xl font-bold text-red-600">Proof not found. It may have been deleted.</h1>`;
88+
}
89+
} catch(e) {
90+
console.error(e);
91+
appDiv.innerHTML = `<p class="text-red-600">Error loading database.</p>`;
92+
}
93+
}
94+
95+
async function updateStatus(id, newStatus) {
96+
await updateDoc(doc(db, "proofs", id), { status: newStatus });
97+
renderCustomerView(id); // Reload the UI to hide buttons
98+
}
99+
100+
// --- ADMIN VIEW ---
101+
function renderAdminView() {
102+
appDiv.innerHTML = `
103+
<h1 class="text-4xl font-black uppercase tracking-tighter mb-8 border-b-4 border-black pb-4 inline-block">Proofing Dashboard</h1>
104+
105+
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200 mb-8">
106+
<h2 class="text-xl font-bold uppercase tracking-wide mb-4 text-gray-700">Generate New Link</h2>
107+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
108+
<input id="custName" type="text" placeholder="Customer Name" class="border-2 border-gray-200 p-3 rounded w-full outline-none focus:border-black transition-colors">
109+
<input id="imgUrl" type="url" placeholder="Direct Image URL (e.g., https://...)" class="border-2 border-gray-200 p-3 rounded w-full outline-none focus:border-black transition-colors">
110+
</div>
111+
<button id="createBtn" class="bg-black text-white px-6 py-3 rounded font-bold uppercase tracking-wide hover:bg-gray-800 transition-colors w-full md:w-auto">Create Proof</button>
112+
</div>
113+
114+
<h2 class="text-2xl font-bold uppercase tracking-tight mb-4">Recent Proofs</h2>
115+
<div id="proofList" class="flex flex-col gap-4"></div>
116+
`;
117+
118+
// Create new proof
119+
document.getElementById('createBtn').addEventListener('click', async () => {
120+
const name = document.getElementById('custName').value;
121+
const url = document.getElementById('imgUrl').value;
122+
if(name && url) {
123+
await addDoc(collection(db, "proofs"), {
124+
customerName: name,
125+
imageUrl: url,
126+
status: 'Pending',
127+
createdAt: new Date()
128+
});
129+
document.getElementById('custName').value = '';
130+
document.getElementById('imgUrl').value = '';
131+
} else {
132+
alert("Please enter both a name and an image URL.");
133+
}
134+
});
135+
136+
// Listen for database changes and render list
137+
onSnapshot(collection(db, "proofs"), (snapshot) => {
138+
const listDiv = document.getElementById('proofList');
139+
listDiv.innerHTML = '';
140+
141+
snapshot.forEach(docSnap => {
142+
const data = docSnap.data();
143+
// Generate the unique link for the customer
144+
const clientLink = window.location.href.split('?')[0] + '?id=' + docSnap.id;
145+
146+
let borderClass = "border-yellow-400";
147+
if(data.status === 'Approved') borderClass = "border-green-500";
148+
if(data.status === 'Denied') borderClass = "border-red-500";
149+
150+
listDiv.innerHTML += `
151+
<div class="bg-white p-5 rounded-lg shadow-sm border-l-8 ${borderClass} flex justify-between items-center">
152+
<div>
153+
<p class="font-black text-xl uppercase tracking-tight">${data.customerName}</p>
154+
<p class="text-sm font-bold text-gray-500 uppercase mb-2">Status: ${data.status}</p>
155+
<div class="flex gap-2">
156+
<a href="${clientLink}" target="_blank" class="text-blue-600 font-bold hover:underline text-sm uppercase">Open Portal</a>
157+
<span class="text-gray-300">|</span>
158+
<button onclick="navigator.clipboard.writeText('${clientLink}'); alert('Link Copied!')" class="text-gray-600 hover:text-black font-bold text-sm uppercase cursor-pointer">Copy Link</button>
159+
</div>
160+
</div>
161+
<img src="${data.imageUrl}" class="h-20 w-20 object-cover rounded shadow-sm border border-gray-200">
162+
</div>
163+
`;
164+
});
165+
});
166+
}
167+
</script>
168+
</body>
169+
</html>

0 commit comments

Comments
 (0)