pramodmisra commited on
Commit
58cac8a
·
verified ·
1 Parent(s): ed4a98d

Upload producer_emails.html

Browse files
app/templates/admin/producer_emails.html ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Producer Emails — Admin{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="space-y-6">
7
+ <div class="flex items-center justify-between">
8
+ <div>
9
+ <h1 class="text-2xl font-bold sw-blue-text">Producer Email Addresses</h1>
10
+ <p class="text-gray-500 text-sm mt-1">
11
+ Assign email addresses to producers so they receive approval requests when listed on a submission.
12
+ </p>
13
+ </div>
14
+ <a href="/admin/?token={{ token }}" class="text-sm text-gray-400 hover:text-gray-600">&larr; Back to Admin</a>
15
+ </div>
16
+
17
+ <!-- Search -->
18
+ <div class="bg-white rounded-lg shadow p-4">
19
+ <input type="text" id="searchBox" placeholder="Search by name or code…"
20
+ oninput="filterProducers()"
21
+ class="w-full border border-gray-200 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400">
22
+ <div class="flex items-center justify-between mt-3 text-xs text-gray-400">
23
+ <span id="countLabel">{{ producers | length }} producers</span>
24
+ <span>
25
+ <span class="inline-block w-3 h-3 rounded-full bg-green-400 mr-1"></span> Has email
26
+ <span class="inline-block w-3 h-3 rounded-full bg-gray-300 ml-3 mr-1"></span> No email
27
+ </span>
28
+ </div>
29
+ </div>
30
+
31
+ <!-- Producer list -->
32
+ <div class="bg-white rounded-lg shadow overflow-hidden">
33
+ <table class="w-full text-sm">
34
+ <thead>
35
+ <tr class="sw-blue text-white text-left">
36
+ <th class="px-4 py-3 w-[5%]">#</th>
37
+ <th class="px-4 py-3 w-[15%]">Code</th>
38
+ <th class="px-4 py-3 w-[25%]">Name</th>
39
+ <th class="px-4 py-3 w-[35%]">Email Address</th>
40
+ <th class="px-4 py-3 w-[20%] text-center">Action</th>
41
+ </tr>
42
+ </thead>
43
+ <tbody id="producerTable">
44
+ {% for p in producers %}
45
+ <tr class="border-b hover:bg-gray-50 producer-row" data-id="{{ p.id }}" data-search="{{ p.name|lower }} {{ p.code|lower }}">
46
+ <td class="px-4 py-3 text-gray-400">{{ loop.index }}</td>
47
+ <td class="px-4 py-3 font-mono text-xs">
48
+ <span class="inline-block w-2 h-2 rounded-full mr-2 {{ 'bg-green-400' if p.email else 'bg-gray-300' }}"></span>
49
+ {{ p.code }}
50
+ </td>
51
+ <td class="px-4 py-3 font-semibold sw-blue-text">{{ p.name }}</td>
52
+ <td class="px-4 py-3">
53
+ <input type="email" id="email_{{ p.id }}"
54
+ value="{{ p.email or '' }}"
55
+ placeholder="name@snellingswalters.com"
56
+ class="w-full border border-gray-200 rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 {{ 'bg-green-50 border-green-200' if p.email else '' }}">
57
+ </td>
58
+ <td class="px-4 py-3 text-center">
59
+ <button onclick="saveEmail({{ p.id }})"
60
+ class="bg-blue-600 text-white px-4 py-1.5 rounded text-xs font-semibold hover:bg-blue-700 transition">
61
+ Save
62
+ </button>
63
+ {% if p.email %}
64
+ <button onclick="clearEmail({{ p.id }})"
65
+ class="text-red-400 hover:text-red-600 text-xs ml-2 transition">
66
+ Clear
67
+ </button>
68
+ {% endif %}
69
+ </td>
70
+ </tr>
71
+ {% endfor %}
72
+ </tbody>
73
+ </table>
74
+ </div>
75
+
76
+ <!-- Bulk paste -->
77
+ <div class="bg-white rounded-lg shadow p-5">
78
+ <h3 class="font-semibold sw-blue-text mb-2">Bulk Import</h3>
79
+ <p class="text-gray-400 text-xs mb-3">Paste CSV lines: <code>PRODUCER_CODE,email@example.com</code> (one per line)</p>
80
+ <textarea id="bulkInput" rows="4" placeholder="KOPAL1,akops@snellingswalters.com&#10;SNEBL1,bsnellings@snellingswalters.com"
81
+ class="w-full border border-gray-200 rounded-lg px-4 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-400"></textarea>
82
+ <button onclick="bulkImport()"
83
+ class="mt-2 bg-green-600 text-white px-6 py-2 rounded-lg text-sm font-semibold hover:bg-green-700 transition">
84
+ Import All
85
+ </button>
86
+ <span id="bulkResult" class="ml-3 text-sm text-gray-500"></span>
87
+ </div>
88
+ </div>
89
+ {% endblock %}
90
+
91
+ {% block scripts %}
92
+ <script>
93
+ const authToken = '{{ token }}';
94
+
95
+ function filterProducers() {
96
+ const q = document.getElementById('searchBox').value.toLowerCase();
97
+ const rows = document.querySelectorAll('.producer-row');
98
+ let visible = 0;
99
+ rows.forEach(r => {
100
+ const match = r.dataset.search.includes(q);
101
+ r.style.display = match ? '' : 'none';
102
+ if (match) visible++;
103
+ });
104
+ document.getElementById('countLabel').textContent = visible + ' producers shown';
105
+ }
106
+
107
+ async function saveEmail(producerId) {
108
+ const input = document.getElementById('email_' + producerId);
109
+ const email = input.value.trim();
110
+
111
+ try {
112
+ const resp = await fetch('/admin/api/producers/' + producerId + '/email', {
113
+ method: 'POST',
114
+ headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + authToken},
115
+ body: JSON.stringify({ email: email }),
116
+ });
117
+ const data = await resp.json();
118
+ if (data.success) {
119
+ input.className = input.className.replace('bg-green-50 border-green-200', '').trim();
120
+ if (email) {
121
+ input.className += ' bg-green-50 border-green-200';
122
+ }
123
+ // Flash green
124
+ input.style.outline = '2px solid #27ae60';
125
+ setTimeout(() => { input.style.outline = ''; }, 1000);
126
+ } else {
127
+ alert('Error: ' + (data.error || 'Unknown'));
128
+ }
129
+ } catch (e) {
130
+ alert('Save failed: ' + e.message);
131
+ }
132
+ }
133
+
134
+ async function clearEmail(producerId) {
135
+ const input = document.getElementById('email_' + producerId);
136
+ input.value = '';
137
+ await saveEmail(producerId);
138
+ }
139
+
140
+ async function bulkImport() {
141
+ const text = document.getElementById('bulkInput').value.trim();
142
+ if (!text) return;
143
+
144
+ const lines = text.split('\n').filter(l => l.trim());
145
+ let success = 0, failed = 0;
146
+
147
+ for (const line of lines) {
148
+ const parts = line.split(',').map(s => s.trim());
149
+ if (parts.length < 2) { failed++; continue; }
150
+
151
+ try {
152
+ const resp = await fetch('/admin/api/producers/email-by-code', {
153
+ method: 'POST',
154
+ headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + authToken},
155
+ body: JSON.stringify({ code: parts[0], email: parts[1] }),
156
+ });
157
+ const data = await resp.json();
158
+ if (data.success) success++;
159
+ else failed++;
160
+ } catch (e) { failed++; }
161
+ }
162
+
163
+ document.getElementById('bulkResult').textContent =
164
+ '✅ ' + success + ' saved, ' + (failed > 0 ? '❌ ' + failed + ' failed' : 'all good!');
165
+
166
+ // Reload to reflect changes
167
+ if (success > 0) {
168
+ setTimeout(() => location.reload(), 1500);
169
+ }
170
+ }
171
+ </script>
172
+ {% endblock %}