nodebb-plugin-simple-contact 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/library.js CHANGED
@@ -4,6 +4,16 @@ const db = require.main.require('./src/database');
4
4
  const notifications = require.main.require('./src/notifications');
5
5
  const groups = require.main.require('./src/groups');
6
6
  const socketIndex = require.main.require('./src/socket.io/index');
7
+ const emailer = require.main.require('./src/emailer');
8
+ const user = require.main.require('./src/user');
9
+
10
+ let chats = null;
11
+ let messaging = null;
12
+
13
+ const possiblePaths = ['./src/chats', './src/messaging'];
14
+
15
+ try { chats = require.main.require('./src/chats'); } catch (e) {}
16
+ try { messaging = require.main.require('./src/messaging'); } catch (e) {}
7
17
 
8
18
  const ContactPlugin = {};
9
19
 
@@ -19,16 +29,115 @@ ContactPlugin.init = async function (params) {
19
29
  router.get('/api/admin/plugins/contact', renderAdminPage);
20
30
  router.post('/api/admin/plugins/contact/handle', middleware.admin.buildHeader, markAsHandled);
21
31
  router.post('/api/admin/plugins/contact/delete', middleware.admin.buildHeader, deleteRequest);
32
+ router.post('/api/admin/plugins/contact/reply', middleware.admin.buildHeader, replyToContact);
33
+ router.post('/api/admin/plugins/contact/chat', middleware.admin.buildHeader, getChatRoom);
22
34
  };
23
35
 
36
+ async function getChatRoom(req, res) {
37
+ if (!chats && !messaging) {
38
+ return res.status(500).json({ error: 'מודולי הצ\'אט לא נמצאו בשרת.' });
39
+ }
40
+
41
+ const { touid, title, firstMessage } = req.body;
42
+ const myUid = req.uid;
43
+
44
+ if (!touid) {
45
+ return res.status(400).json({ error: 'חסר מזהה משתמש (UID)' });
46
+ }
47
+
48
+ try {
49
+ let roomId = null;
50
+
51
+ const chatModule = chats || messaging; // עדיפות ל-chats ליצירת חדר
52
+
53
+ if (chatModule.getRoomId) {
54
+ roomId = await chatModule.getRoomId(myUid, touid);
55
+ } else if (chatModule.getRoomIdForUser) {
56
+ roomId = await chatModule.getRoomIdForUser(myUid, touid);
57
+ }
58
+
59
+ if (!roomId) {
60
+ if (chatModule.create) {
61
+ const newRoom = await chatModule.create([myUid, touid]);
62
+ roomId = (newRoom && newRoom.roomId) ? newRoom.roomId : newRoom;
63
+ } else if (chatModule.newRoom) {
64
+ roomId = await chatModule.newRoom(myUid, [touid]);
65
+ } else if (chatModule.createRoom) {
66
+ roomId = await chatModule.createRoom([myUid, touid]);
67
+ }
68
+ }
69
+
70
+ if (!roomId) {
71
+ throw new Error('לא ניתן היה ליצור את החדר');
72
+ }
73
+
74
+ if (typeof roomId === 'object' && roomId.roomId) {
75
+ roomId = roomId.roomId;
76
+ }
77
+
78
+ if (title) {
79
+ try {
80
+ if (chats && chats.renameRoom) {
81
+ await chats.renameRoom(myUid, roomId, title);
82
+ } else if (messaging && messaging.renameRoom) {
83
+ await messaging.renameRoom(myUid, roomId, title);
84
+ }
85
+ } catch (err) {
86
+ console.warn('[Contact Plugin] Failed to rename room:', err.message);
87
+ }
88
+ }
89
+
90
+ if (firstMessage) {
91
+ let sent = false;
92
+ console.log('[Contact Plugin] Attempting to send message to Room ' + roomId);
93
+
94
+ if (!sent && messaging && messaging.sendMessage) {
95
+ try {
96
+ await messaging.sendMessage({ uid: myUid, roomId: roomId, content: firstMessage });
97
+ sent = true;
98
+ console.log('[Contact Plugin] Sent via messaging.sendMessage (Object)');
99
+ } catch (e) { console.log('Try 1 failed:', e.message); }
100
+ }
101
+
102
+ if (!sent && chats && chats.addMessage) {
103
+ try {
104
+ await chats.addMessage(myUid, roomId, firstMessage);
105
+ sent = true;
106
+ console.log('[Contact Plugin] Sent via chats.addMessage');
107
+ } catch (e) { console.log('Try 2 failed:', e.message); }
108
+ }
109
+
110
+ if (!sent && chats && chats.send) {
111
+ try {
112
+ await chats.send(myUid, roomId, firstMessage);
113
+ sent = true;
114
+ console.log('[Contact Plugin] Sent via chats.send');
115
+ } catch (e) { console.log('Try 3 failed:', e.message); }
116
+ }
117
+
118
+ if (!sent && chats && chats.reply) {
119
+ try {
120
+ await chats.reply(roomId, myUid, firstMessage);
121
+ sent = true;
122
+ console.log('[Contact Plugin] Sent via chats.reply');
123
+ } catch (e) { console.log('Try 4 failed:', e.message); }
124
+ }
125
+
126
+ if (!sent) {
127
+ console.error('[Contact Plugin] Failed to send message with all known methods.');
128
+ }
129
+ }
130
+
131
+ res.json({ roomId: roomId });
132
+
133
+ } catch (err) {
134
+ console.error('[Contact Plugin Chat Error]', err);
135
+ res.status(500).json({ error: 'שגיאה פנימית: ' + err.message });
136
+ }
137
+ }
138
+
24
139
  async function renderContactPage(req, res) {
25
- res.render('contact', {
26
- title: 'צור קשר',
27
- breadcrumbs: [
28
- { text: 'דף הבית', url: '/' },
29
- { text: 'צור קשר' }
30
- ]
31
- });
140
+ res.render('contact', { title: 'צור קשר', breadcrumbs: [{ text: 'דף הבית', url: '/' }, { text: 'צור קשר' }] });
32
141
  }
33
142
 
34
143
  async function handleContactSubmission(req, res) {
@@ -39,11 +148,13 @@ async function handleContactSubmission(req, res) {
39
148
 
40
149
  const contactId = Date.now();
41
150
  const key = 'contact-request:' + contactId;
151
+ const senderUid = req.uid || 0;
42
152
 
43
153
  const contactData = {
44
154
  id: contactId,
45
155
  fullName: data.fullName,
46
156
  username: data.username || 'אורח',
157
+ uid: senderUid,
47
158
  email: data.email,
48
159
  content: data.content,
49
160
  timestamp: contactId,
@@ -55,35 +166,19 @@ async function handleContactSubmission(req, res) {
55
166
  await db.sortedSetAdd('contact-requests:sorted', contactId, contactId);
56
167
 
57
168
  const adminUids = await groups.getMembers('administrators', 0, -1);
58
-
59
169
  if (adminUids && adminUids.length > 0) {
60
170
  await Promise.all(adminUids.map(async (uid) => {
61
171
  const userUnreadKey = 'contact:unread_names:' + uid;
62
172
  const userNid = 'contact:notification:' + uid;
63
-
64
173
  await db.listAppend(userUnreadKey, contactData.fullName);
65
174
  const myNames = await db.getListRange(userUnreadKey, 0, -1);
66
-
67
- let notificationTitle = '';
68
- if (myNames.length === 1) {
69
- notificationTitle = `פנייה חדשה מאת ${myNames[0]}`;
70
- } else {
71
- notificationTitle = `${myNames.length} פניות חדשות מאת: ${myNames.join(', ')}`;
72
- }
73
-
175
+ let notificationTitle = (myNames.length === 1) ? `פנייה חדשה מאת ${myNames[0]}` : `${myNames.length} פניות חדשות מאת: ${myNames.join(', ')}`;
74
176
  const notification = await notifications.create({
75
- type: 'new-contact',
76
- bodyShort: notificationTitle,
77
- bodyLong: contactData.content,
78
- nid: userNid,
79
- path: '/admin/plugins/contact',
80
- from: 0
177
+ type: 'new-contact', bodyShort: notificationTitle, bodyLong: contactData.content, nid: userNid, path: '/admin/plugins/contact', from: senderUid
81
178
  });
82
-
83
179
  await notifications.push(notification, [uid]);
84
180
  }));
85
181
  }
86
-
87
182
  res.json({ success: true, message: 'הפנייה נשלחה בהצלחה.' });
88
183
  } catch (err) {
89
184
  console.error(err);
@@ -96,90 +191,45 @@ async function renderAdminPage(req, res) {
96
191
  try {
97
192
  const userUnreadKey = 'contact:unread_names:' + req.uid;
98
193
  const userNid = 'contact:notification:' + req.uid;
99
-
100
194
  await notifications.markRead(userNid, req.uid);
101
-
102
195
  await db.delete(userUnreadKey);
103
-
104
- const unreadCount = await notifications.getUnreadCount(req.uid);
105
-
106
- socketIndex.in('uid:' + req.uid).emit('event:unread.updateCount', unreadCount);
107
-
108
- socketIndex.in('uid:' + req.uid).emit('event:notifications.updateCount', unreadCount);
109
-
110
- } catch (e) {
111
- console.error('Error handling notifications logic', e);
112
- }
196
+ } catch (e) { console.error('Error handling notifications logic', e); }
113
197
  }
114
-
115
198
  const ids = await db.getSortedSetRevRange('contact-requests:sorted', 0, -1);
116
- let items = [];
117
-
118
- if (ids.length > 0) {
119
- const keys = ids.map(id => 'contact-request:' + id);
120
- items = await db.getObjects(keys);
121
- }
122
-
123
- const waitingRequests = [];
124
- const handledRequests = [];
199
+ let items = (ids.length > 0) ? await db.getObjects(ids.map(id => 'contact-request:' + id)) : [];
200
+ const waitingRequests = [], handledRequests = [];
125
201
 
126
- items.forEach(item => {
202
+ for (const item of items) {
127
203
  item.date = new Date(parseInt(item.timestamp)).toLocaleString();
128
- if (item.handled) {
129
- handledRequests.push(item);
130
- } else {
131
- waitingRequests.push(item);
132
- }
133
- });
134
-
135
- res.render('admin/plugins/contact', {
136
- waitingRequests: waitingRequests,
137
- handledRequests: handledRequests
138
- });
204
+ if ((!item.uid || parseInt(item.uid) === 0) && item.username && item.username !== 'אורח') {
205
+ try {
206
+ const foundUid = await user.getUidByUsername(item.username);
207
+ item.uid = foundUid ? parseInt(foundUid) : 0;
208
+ } catch (err) { item.uid = 0; }
209
+ } else { item.uid = parseInt(item.uid) || 0; }
210
+ item.showChat = (item.uid > 0);
211
+ if (item.handled) handledRequests.push(item); else waitingRequests.push(item);
212
+ }
213
+ res.render('admin/plugins/contact', { waitingRequests: waitingRequests, handledRequests: handledRequests });
139
214
  }
140
215
 
141
216
  async function markAsHandled(req, res) {
142
- const id = req.body.id;
143
- try {
144
- await db.setObjectField('contact-request:' + id, 'handled', true);
145
- res.json({ success: true });
146
- } catch (err) {
147
- res.status(500).json({ error: err.message });
148
- }
217
+ try { await db.setObjectField('contact-request:' + req.body.id, 'handled', true); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); }
149
218
  }
150
-
151
219
  async function deleteRequest(req, res) {
152
- const id = req.body.id;
220
+ try { await db.delete('contact-request:' + req.body.id); await db.sortedSetRemove('contact-requests:sorted', req.body.id); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); }
221
+ }
222
+ async function replyToContact(req, res) {
223
+ const { email, subject, content, original_id } = req.body;
224
+ if (!email || !content) return res.status(400).json({ error: 'חסרים נתונים לשליחה' });
153
225
  try {
154
- await db.delete('contact-request:' + id);
155
- await db.sortedSetRemove('contact-requests:sorted', id);
226
+ await emailer.sendToEmail('banned', email, 'he', { subject: subject || 'תשובה', username: email, message: content.replace(/\n/g, '<br>'), body: content.replace(/\n/g, '<br>') });
227
+ if (original_id) await db.setObjectField('contact-request:' + original_id, 'handled', true);
156
228
  res.json({ success: true });
157
- } catch (err) {
158
- res.status(500).json({ error: err.message });
159
- }
229
+ } catch (err) { res.status(500).json({ error: err.message }); }
160
230
  }
161
231
 
162
- ContactPlugin.addNavigation = async function (header) {
163
- if (header && Array.isArray(header.navigation)) {
164
- header.navigation.push({
165
- route: '/contact',
166
- iconClass: 'fa-envelope',
167
- text: 'צור קשר',
168
- title: 'צור קשר'
169
- });
170
- }
171
- return header;
172
- };
173
-
174
- ContactPlugin.addAdminNavigation = async function (header) {
175
- if (header && Array.isArray(header.plugins)) {
176
- header.plugins.push({
177
- route: '/plugins/contact',
178
- icon: 'fa-envelope',
179
- name: 'פניות צור קשר'
180
- });
181
- }
182
- return header;
183
- };
232
+ ContactPlugin.addNavigation = async function (header) { if (header && Array.isArray(header.navigation)) { header.navigation.push({ route: '/contact', iconClass: 'fa-envelope', text: 'צור קשר', title: 'צור קשר' }); } return header; };
233
+ ContactPlugin.addAdminNavigation = async function (header) { if (header && Array.isArray(header.plugins)) { header.plugins.push({ route: '/plugins/contact', icon: 'fa-envelope', name: 'פניות צור קשר' }); } return header; };
184
234
 
185
235
  module.exports = ContactPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-simple-contact",
3
- "version": "1.2.2",
3
+ "version": "2.0.0",
4
4
  "description": "תוסף טופס צור קשר עם התראות למנהלים וניהול פניות",
5
5
  "main": "library.js",
6
6
  "nbbpm": {
@@ -26,7 +26,15 @@
26
26
  <tr data-id="{./id}" class="text-center">
27
27
  <td>{./date}</td>
28
28
  <td><strong>{./fullName}</strong></td>
29
- <td>{./username}</td>
29
+ <td>
30
+ {{{ if ./showChat }}}
31
+ <a href="{config.relative_path}/admin/manage/users?searchBy=uid&query={./uid}&page=1&sortBy=lastonline" target="_blank" title="ניהול משתמש">
32
+ {./username}
33
+ </a>
34
+ {{{ else }}}
35
+ {./username}
36
+ {{{ end }}}
37
+ </td>
30
38
  <td><a href="mailto:{./email}">{./email}</a></td>
31
39
  <td style="max-width:260px;">
32
40
  <div class="toggle-text" style="white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer;" title="לחץ להצגה/הסתרה">
@@ -36,10 +44,22 @@
36
44
  <td>
37
45
  <span class="label label-warning" style="border-radius:10px; padding:3px 6px; font-size:11px;">ממתין</span>
38
46
  </td>
39
- <td class="text-center">
40
- <button class="btn btn-xs btn-primary mark-handled" data-id="{./id}" style="border-radius:14px; font-size:11px; padding:3px 8px;">
41
- סמן כטופל
42
- </button>
47
+ <td class="text-left">
48
+ <div class="btn-group btn-group-sm">
49
+ {{{ if ./showChat }}}
50
+ <button class="btn btn-success reply-chat" data-uid="{./uid}" style="margin-left:2px; border-radius:4px;">
51
+ <i class="fa fa-comments"></i> השב בצאט
52
+ </button>
53
+ {{{ end }}}
54
+
55
+ <button class="btn btn-info reply-email" data-id="{./id}" data-email="{./email}" style="margin-left:2px; border-radius:4px;">
56
+ <i class="fa fa-envelope-o"></i> השב במייל
57
+ </button>
58
+
59
+ <button class="btn btn-primary mark-handled" data-id="{./id}" style="border-radius:4px;">
60
+ ✓ סמן כטופל
61
+ </button>
62
+ </div>
43
63
  </td>
44
64
  </tr>
45
65
  {{{ end }}}
@@ -81,7 +101,15 @@
81
101
  <tr data-id="{./id}" class="text-center">
82
102
  <td>{./date}</td>
83
103
  <td><strong>{./fullName}</strong></td>
84
- <td>{./username}</td>
104
+ <td>
105
+ {{{ if ./showChat }}}
106
+ <a href="{config.relative_path}/admin/manage/users?searchBy=uid&query={./uid}&page=1&sortBy=lastonline" target="_blank" title="ניהול משתמש">
107
+ {./username}
108
+ </a>
109
+ {{{ else }}}
110
+ {./username}
111
+ {{{ end }}}
112
+ </td>
85
113
  <td><a href="mailto:{./email}">{./email}</a></td>
86
114
  <td style="max-width:260px;">
87
115
  <div class="toggle-text" style="white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer;" title="לחץ להצגה/הסתרה">
@@ -108,8 +136,69 @@
108
136
  {{{ end }}}
109
137
  </div>
110
138
  </div>
139
+ </div>
140
+ </div>
111
141
 
142
+ <div id="emailReplyModal" class="modal fade" tabindex="-1" role="dialog">
143
+ <div class="modal-dialog" role="document">
144
+ <div class="modal-content">
145
+ <div class="modal-header">
146
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
147
+ <h4 class="modal-title">תשובה לפנייה במייל</h4>
148
+ </div>
149
+ <div class="modal-body">
150
+ <form id="emailReplyForm">
151
+ <input type="hidden" id="reply-id" name="id">
152
+ <input type="hidden" id="reply-to">
153
+ <div class="form-group">
154
+ <label>נושא:</label>
155
+ <input type="text" class="form-control" id="reply-subject" value="תשובה לפנייתך בצור קשר">
156
+ </div>
157
+ <div class="form-group">
158
+ <label>תוכן ההודעה:</label>
159
+ <textarea class="form-control" id="reply-content" rows="6"></textarea>
160
+ </div>
161
+ </form>
162
+ </div>
163
+ <div class="modal-footer">
164
+ <button type="button" class="btn btn-default" data-dismiss="modal">ביטול</button>
165
+ <button type="button" class="btn btn-primary" id="send-reply-btn">שלח מייל</button>
166
+ </div>
112
167
  </div>
168
+ </div>
169
+ </div>
170
+
171
+ <div id="chatReplyModal" class="modal fade" tabindex="-1" role="dialog">
172
+ <div class="modal-dialog" role="document">
173
+ <div class="modal-content">
174
+ <div class="modal-header">
175
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
176
+ <h4 class="modal-title">פתיחת צ'אט ושליחת הודעה</h4>
177
+ </div>
178
+ <div class="modal-body">
179
+ <form id="chatReplyForm">
180
+ <input type="hidden" id="chat-uid">
181
+ <input type="hidden" id="chat-original-id"> <div class="form-group">
182
+ <label>כותרת הצ'אט:</label>
183
+ <input type="text" class="form-control" id="chat-title-input">
184
+ <p class="help-block" style="font-size:12px;">שם החדר ישתנה לכותרת זו.</p>
185
+ </div>
186
+
187
+ <div class="form-group">
188
+ <label>הודעה:</label>
189
+ <textarea class="form-control" id="chat-message-input" rows="4"></textarea>
190
+ <p class="help-block" style="font-size:12px;">הודעה זו תישלח אוטומטית למשתמש.</p>
191
+ </div>
192
+ </form>
193
+ </div>
194
+ <div class="modal-footer">
195
+ <button type="button" class="btn btn-default" data-dismiss="modal">ביטול</button>
196
+ <button type="button" class="btn btn-success" id="create-chat-btn">
197
+ <i class="fa fa-paper-plane"></i> שלח הודעת צ'אט
198
+ </button>
199
+ </div>
200
+ </div>
201
+ </div>
113
202
  </div>
114
203
 
115
204
  <script>
@@ -123,17 +212,7 @@
123
212
  if (window.app && typeof window.app.require === 'function') {
124
213
  window.app.require('alerts').then(function(alerts) {
125
214
  type === 'success' ? alerts.success(clean) : alerts.error(clean);
126
- }).catch(function() {
127
- alert(clean);
128
- });
129
- return;
130
- }
131
- const req = window.require || window.requirejs;
132
- if (req) {
133
- req(['alerts'], function(alerts) {
134
- if (!alerts) return alert(clean);
135
- type === 'success' ? alerts.success(clean) : alerts.error(clean);
136
- });
215
+ }).catch(function() { alert(clean); });
137
216
  return;
138
217
  }
139
218
  alert(clean);
@@ -152,6 +231,108 @@
152
231
  }
153
232
 
154
233
  function initContactPage() {
234
+
235
+ $('.reply-chat').off('click').on('click', function(e) {
236
+ e.preventDefault();
237
+ var btn = $(this);
238
+ var uid = btn.data('uid') || btn.attr('data-uid');
239
+ var originalId = btn.closest('tr').attr('data-id');
240
+
241
+ if (!uid || uid == 0) {
242
+ return showMessage('error', 'שגיאה: מזהה משתמש (UID) חסר.');
243
+ }
244
+
245
+ var row = btn.closest('tr');
246
+ var fullName = row.find('strong').text().trim();
247
+ var contentDiv = row.find('.toggle-text');
248
+ var rawContent = contentDiv.text().trim();
249
+ var shortContent = rawContent.length > 50 ? rawContent.substring(0, 50) + '...' : rawContent;
250
+
251
+ $('#chat-uid').val(uid);
252
+ $('#chat-original-id').val(originalId);
253
+ $('#chat-title-input').val('בעניין פנייתך בצור קשר');
254
+ $('#chat-message-input').val('שלום ,\nכהמשך לפנייתך בצור קשר.\nתשובתינו היא:');
255
+
256
+ $('#chatReplyModal').modal('show');
257
+ });
258
+
259
+ $('#create-chat-btn').off('click').on('click', function() {
260
+ var btn = $(this);
261
+ var uid = $('#chat-uid').val();
262
+ var title = $('#chat-title-input').val();
263
+ var message = $('#chat-message-input').val();
264
+
265
+ if (!uid) return;
266
+
267
+ btn.prop('disabled', true);
268
+
269
+ $.post(config.relative_path + '/api/admin/plugins/contact/chat', {
270
+ touid: uid,
271
+ title: title,
272
+ firstMessage: message,
273
+ _csrf: config.csrf_token
274
+ })
275
+ .done(function(data) {
276
+ $('#chatReplyModal').modal('hide');
277
+
278
+ if (data && data.roomId) {
279
+ showMessage('success', "הודעת הצ'אט נשלחה בהצלחה");
280
+
281
+ // setTimeout(function() { ajaxify.refresh(); }, 1500);
282
+ } else {
283
+ showMessage('error', 'לא התקבל מזהה חדר מהשרת, ייתכן וההודעה לא נשלחה.');
284
+ }
285
+ })
286
+ .fail(function(xhr) {
287
+ var err = xhr.responseJSON ? xhr.responseJSON.error : 'שגיאה בשליחת ההודעה';
288
+ showMessage('error', err);
289
+ })
290
+ .always(function() {
291
+ btn.prop('disabled', false);
292
+ });
293
+ });
294
+
295
+ $('.reply-email').off('click').on('click', function() {
296
+ var email = $(this).data('email');
297
+ var id = $(this).data('id');
298
+ $('#reply-to').val(email);
299
+ $('#reply-id').val(id);
300
+ $('#reply-content').val('');
301
+ $('#emailReplyModal').modal('show');
302
+ });
303
+
304
+ $('#send-reply-btn').off('click').on('click', function() {
305
+ var btn = $(this);
306
+ var email = $('#reply-to').val();
307
+ var subject = $('#reply-subject').val();
308
+ var content = $('#reply-content').val();
309
+ var id = $('#reply-id').val();
310
+
311
+ if (!content) return showMessage('error', 'נא לכתוב תוכן להודעה');
312
+
313
+ btn.prop('disabled', true);
314
+
315
+ $.post(config.relative_path + '/api/admin/plugins/contact/reply', {
316
+ email: email,
317
+ subject: subject,
318
+ content: content,
319
+ original_id: id,
320
+ _csrf: config.csrf_token
321
+ })
322
+ .done(function(data) {
323
+ $('#emailReplyModal').modal('hide');
324
+ showMessage('success', 'המייל נשלח בהצלחה והפנייה סומנה כטופלה');
325
+ setTimeout(function() { ajaxify.refresh(); }, 1000);
326
+ })
327
+ .fail(function(xhr) {
328
+ var err = xhr.responseJSON ? xhr.responseJSON.error : 'שגיאה בשליחה';
329
+ showMessage('error', err);
330
+ })
331
+ .always(function() {
332
+ btn.prop('disabled', false);
333
+ });
334
+ });
335
+
155
336
  $('.mark-handled').off('click').on('click', function() {
156
337
  var id = $(this).data('id');
157
338
  confirmBox('האם לסמן פנייה זו כטופלה?', function(ok) {
@@ -169,51 +350,24 @@
169
350
  $('.toggle-text').off('click').on('click', function() {
170
351
  var $el = $(this);
171
352
  if ($el.css('white-space') === 'nowrap') {
172
- $el.css({
173
- 'white-space': 'normal',
174
- 'word-break': 'break-word',
175
- 'overflow': 'visible'
176
- });
353
+ $el.css({ 'white-space': 'normal', 'word-break': 'break-word', 'overflow': 'visible' });
177
354
  } else {
178
- $el.css({
179
- 'white-space': 'nowrap',
180
- 'overflow': 'hidden',
181
- 'text-overflow': 'ellipsis'
182
- });
355
+ $el.css({ 'white-space': 'nowrap', 'overflow': 'hidden', 'text-overflow': 'ellipsis' });
183
356
  }
184
357
  });
358
+
359
+ $('.modal').on('click', '[data-dismiss="modal"]', function(e) {
360
+ e.preventDefault();
361
+ $(this).closest('.modal').modal('hide');
362
+ });
185
363
  }
186
364
 
187
365
  function performHandle(id) {
188
- $.post(config.relative_path + '/api/admin/plugins/contact/handle', {
189
- id: id,
190
- _csrf: config.csrf_token
191
- })
192
- .done(function(data) {
193
- if (data.success) {
194
- showMessage('success', 'הפנייה סומנה כטופלה בהצלחה');
195
- ajaxify.refresh();
196
- }
197
- })
198
- .fail(function() {
199
- showMessage('error', 'שגיאה בעדכון הפנייה');
200
- });
366
+ $.post(config.relative_path + '/api/admin/plugins/contact/handle', { id: id, _csrf: config.csrf_token }).done(function(data) { if (data.success) { showMessage('success', 'הפנייה סומנה כטופלה בהצלחה'); ajaxify.refresh(); } }).fail(function() { showMessage('error', 'שגיאה בעדכון הפנייה'); });
201
367
  }
202
368
 
203
369
  function performDelete(id) {
204
- $.post(config.relative_path + '/api/admin/plugins/contact/delete', {
205
- id: id,
206
- _csrf: config.csrf_token
207
- })
208
- .done(function(data) {
209
- if (data.success) {
210
- showMessage('success', 'הפנייה נמחקה בהצלחה');
211
- ajaxify.refresh();
212
- }
213
- })
214
- .fail(function() {
215
- showMessage('error', 'שגיאה במחיקת הפנייה');
216
- });
370
+ $.post(config.relative_path + '/api/admin/plugins/contact/delete', { id: id, _csrf: config.csrf_token }).done(function(data) { if (data.success) { showMessage('success', 'הפנייה נמחקה בהצלחה'); ajaxify.refresh(); } }).fail(function() { showMessage('error', 'שגיאה במחיקת הפנייה'); });
217
371
  }
218
372
 
219
373
  $(document).ready(initContactPage);
@@ -223,4 +377,4 @@
223
377
  }
224
378
  });
225
379
  })();
226
- </script>
380
+ </script>
@@ -26,8 +26,8 @@
26
26
  </div>
27
27
 
28
28
  <div class="form-group" style="margin-bottom:20px;">
29
- <label for="content" style="font-weight:600; display:block; margin-bottom:6px;">תוכן הפנייה *</label>
30
- <textarea class="form-control" id="content" name="content" rows="6" required style="border-radius:10px; padding:10px;"></textarea>
29
+ <label for="contact-message" style="font-weight:600; display:block; margin-bottom:6px;">תוכן הפנייה *</label>
30
+ <textarea class="form-control" id="contact-message" name="content" rows="6" required style="border-radius:10px; padding:10px;"></textarea>
31
31
  </div>
32
32
 
33
33
  <button type="submit" class="btn btn-primary btn-block" id="submit-btn" style="border-radius:22px; font-weight:600; padding:12px; margin-top:10px;">
@@ -45,38 +45,64 @@
45
45
 
46
46
  <script>
47
47
  (function() {
48
- var init = function() {
48
+ var loadContactForm = function() {
49
+ if (typeof window.jQuery === 'undefined' || typeof window.app === 'undefined') {
50
+ setTimeout(loadContactForm, 200);
51
+ return;
52
+ }
53
+
54
+ var $ = window.jQuery;
49
55
  var form = $('#contact-form');
56
+ if (!form.length) return;
57
+
50
58
  var btn = $('#submit-btn');
51
59
  var alertBox = $('#contact-alert');
60
+ var isLoggedIn = (app.user && app.user.uid > 0);
52
61
 
53
- if (window.app && app.user && app.user.uid > 0) {
62
+ if (isLoggedIn) {
54
63
  $('#username-group').hide();
55
64
  }
56
65
 
57
66
  form.off('submit').on('submit', function(e) {
58
67
  e.preventDefault();
68
+
69
+ var contentVal = $('#contact-message').val();
70
+ var fullNameVal = $('#fullName').val();
71
+ var emailVal = $('#email').val();
72
+ var usernameVal = isLoggedIn ? app.user.username : $('#username').val();
73
+
74
+ if (!contentVal || contentVal.trim() === '') {
75
+ alertBox.removeClass('alert-success').addClass('alert-danger').text('אנא כתוב תוכן לפנייה').fadeIn();
76
+ return;
77
+ }
78
+
59
79
  btn.prop('disabled', true).text('שולח...');
60
80
  alertBox.hide().removeClass('alert-success alert-danger');
61
81
 
62
- var formData = {
63
- fullName: $('#fullName').val(),
64
- username: (app.user && app.user.uid > 0) ? app.user.username : $('#username').val(),
65
- email: $('#email').val(),
66
- content: $('#content').val(),
82
+ var payload = {
83
+ fullName: fullNameVal,
84
+ email: emailVal,
85
+ content: contentVal,
86
+ username: usernameVal,
67
87
  _csrf: config.csrf_token
68
88
  };
69
89
 
90
+ console.log('Sending Payload:', payload);
91
+
70
92
  $.ajax({
71
93
  url: config.relative_path + '/api/contact/send',
72
94
  type: 'POST',
73
- data: formData,
95
+ data: payload,
96
+ headers: {
97
+ 'x-csrf-token': config.csrf_token
98
+ },
74
99
  success: function(response) {
75
100
  alertBox.addClass('alert-success').text(response.message || 'הפנייה נשלחה בהצלחה').fadeIn();
76
101
  form[0].reset();
77
102
  },
78
103
  error: function(xhr) {
79
- var msg = (xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'אירעה שגיאה בשליחה';
104
+ console.error('Error:', xhr);
105
+ var msg = (xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'אירעה שגיאה בשליחה. נסה שוב.';
80
106
  alertBox.addClass('alert-danger').text(msg).fadeIn();
81
107
  },
82
108
  complete: function() {
@@ -86,16 +112,14 @@
86
112
  });
87
113
  };
88
114
 
89
- if (typeof jQuery !== 'undefined') {
90
- init();
91
- } else {
92
- window.addEventListener('DOMContentLoaded', init);
115
+ loadContactForm();
116
+
117
+ if (typeof window.jQuery !== 'undefined') {
118
+ $(window).on('action:ajaxify.end', function(ev, data) {
119
+ if (data.url.indexOf('contact') !== -1) {
120
+ loadContactForm();
121
+ }
122
+ });
93
123
  }
94
-
95
- $(window).on('action:ajaxify.end', function(ev, data) {
96
- if (data.url === 'contact' || data.url === 'forum/contact') {
97
- init();
98
- }
99
- });
100
124
  })();
101
125
  </script>