xgit simple git

nexus

nexus

clone git clone https://kb.hax.al/nexus

users/profile.php

1 <?php
2 require_once __DIR__ . '/../includes/bootstrap.php';
3 $uname   = get('u');
4 $profile = DB::row('SELECT * FROM users WHERE username=?', [$uname]);
5 if (!$profile) render_404();
6 
7 $topics = DB::rows("
8     SELECT t.*,c.name AS cat_name,c.slug AS cat_slug
9     FROM topics t JOIN categories c ON c.id=t.category_id
10     WHERE t.user_id=? ORDER BY t.created_at DESC LIMIT 15
11 ", [$profile['id']]);
12 
13 $posts = DB::rows("
14     SELECT p.*,t.title AS topic_title,t.slug AS topic_slug
15     FROM posts p JOIN topics t ON t.id=p.topic_id
16     WHERE p.user_id=? AND p.deleted=0 ORDER BY p.created_at DESC LIMIT 15
17 ", [$profile['id']]);
18 
19 $showFriends = !$profile['friends_hidden']
20     || ($USER && (int)$USER['id'] === (int)$profile['id'])
21     || ($USER && is_admin());
22 
23 $friendsList = $showFriends ? friends_list((int)$profile['id']) : [];
24 $fStatus     = $USER ? friend_status((int)$USER['id'], (int)$profile['id']) : 'none';
25 $kTier       = karma_tier((int)$profile['karma']);
26 
27 $PAGE_TITLE = '@' . $profile['username'];
28 include __DIR__ . '/../views/partials/layout.php';
29 ?>
30 
31 <div class="profile-hdr">
32   <!-- Avatar -->
33   <div class="profile-av">
34     <?php if ($profile['avatar']): ?>
35       <img src="<?= e($profile['avatar']) ?>" class="av-xl" alt="">
36     <?php else: ?>
37       <span class="av-xl av-init"><?= strtoupper($profile['username'][0]) ?></span>
38     <?php endif; ?>
39     <!-- Karma tier badge under avatar โ€” green leaf -->
40     <div class="profile-tier-badge profile-tier-badge-green">
41       <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" width="11" height="11"><path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10z"/><path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12"/></svg>
42       <?= e($kTier['name']) ?>
43     </div>
44   </div>
45 
46   <!-- Info -->
47   <div class="profile-info">
48     <div class="profile-name-row">
49       <h1>@<?= e($profile['username']) ?></h1>
50       <span class="role-tag role-<?= e($profile['role']) ?>"><?= e($profile['role']) ?></span>
51       <?php if ($profile['suspended']): ?>
52         <span class="role-tag role-admin">Suspended</span>
53       <?php endif; ?>
54     </div>
55     <?php if ($profile['bio']): ?>
56       <p class="profile-bio"><?= e($profile['bio']) ?></p>
57     <?php endif; ?>
58     <div class="profile-meta">
59       <span>๐Ÿ“… Joined <span class="ago" data-ts="<?= e($profile['joined_at']) ?>"></span></span>
60       <span>๐Ÿ• Last seen <span class="ago" data-ts="<?= e($profile['last_seen']) ?>"></span></span>
61       <?php if (!empty($profile['location'])): ?>
62         <span>
63           <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="13" height="13" style="vertical-align:middle"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
64           <?= e($profile['location']) ?>
65         </span>
66       <?php endif; ?>
67     </div>
68 
69     <!-- Karma bar โ€” always green -->
70     <div class="profile-karma-row">
71       <div class="pkr-head">
72         <span class="pkr-icon">
73           <svg viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" width="16" height="16"><path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10z"/><path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12"/></svg>
74         </span>
75         <span class="pkr-label pkr-label-green"><?= e($kTier['name']) ?></span>
76         <span class="pkr-pts"><?= number_format((int)$profile['karma']) ?> karma</span>
77       </div>
78       <div class="pkr-bar">
79         <div class="pkr-fill pkr-fill-green" style="width:<?= $kTier['progress'] ?>%"></div>
80       </div>
81       <?php if ($kTier['next'] !== null): ?>
82         <div class="pkr-next">
83           <?= $kTier['progress'] ?>% ยท <?= number_format($kTier['next'] - (int)$profile['karma']) ?> more to <?= e(karma_tier($kTier['next'])['name']) ?>
84         </div>
85       <?php else: ?>
86         <div class="pkr-next">๐Ÿ† Maximum tier reached!</div>
87       <?php endif; ?>
88     </div>
89   </div>
90 
91   <!-- Stats -->
92   <div class="profile-stats">
93     <div class="pstat">
94       <strong><?= number_format($profile['post_count']) ?></strong>
95       <span>Posts</span>
96     </div>
97     <div class="pstat">
98       <strong><?= number_format($profile['topic_count']) ?></strong>
99       <span>Topics</span>
100     </div>
101     <div class="pstat karma-stat" style="--kc:<?= e($kTier['color']) ?>">
102       <strong><?= number_format((int)$profile['karma']) ?></strong>
103       <span><?= $kTier['icon'] ?> Karma</span>
104     </div>
105     <div class="pstat">
106       <strong><?= count($friendsList) ?></strong>
107       <span>Friends</span>
108     </div>
109   </div>
110 
111   <!-- Actions -->
112   <div class="profile-actions">
113     <?php if ($USER && $USER['username'] === $profile['username']): ?>
114       <a href="<?= u('users/edit.php') ?>" class="btn-ghost btn-sm">โœ๏ธ Edit Profile</a>
115       <a href="<?= u('messages/') ?>" class="btn-ghost btn-sm">๐Ÿ“ฌ Messages</a>
116     <?php elseif ($USER): ?>
117       <?php if ($fStatus === 'none'): ?>
118         <button class="btn-primary btn-sm" onclick="friendAction('send',<?= $profile['id'] ?>,this)">+ Add Friend</button>
119       <?php elseif ($fStatus === 'pending_sent'): ?>
120         <button class="btn-ghost btn-sm" onclick="friendAction('cancel',<?= $profile['id'] ?>,this)">โœ“ Sent (Cancel)</button>
121       <?php elseif ($fStatus === 'pending_received'): ?>
122         <button class="btn-primary btn-sm" onclick="friendAction('accept',<?= $profile['id'] ?>,this)">โœ“ Accept</button>
123         <button class="btn-ghost btn-sm"   onclick="friendAction('decline',<?= $profile['id'] ?>,this)">โœ— Decline</button>
124       <?php elseif ($fStatus === 'friends'): ?>
125         <button class="btn-ghost btn-sm" onclick="friendAction('remove',<?= $profile['id'] ?>,this)">๐Ÿ‘ฅ Friends</button>
126       <?php endif; ?>
127       <a href="<?= u('messages/compose.php?to='.urlencode($profile['username'])) ?>" class="btn-ghost btn-sm">โœ‰๏ธ Message</a>
128     <?php endif; ?>
129 
130     <?php if ($USER && is_admin() && (int)$USER['id'] !== (int)$profile['id']): ?>
131       <a href="<?= u('admin/user.php?id='.$profile['id']) ?>" class="btn-ghost btn-sm">๐Ÿ›ก๏ธ Admin</a>
132     <?php endif; ?>
133   </div>
134 </div>
135 
136 <!-- Pending friend requests (profile owner only) -->
137 <?php if ($USER && (int)$USER['id'] === (int)$profile['id']): ?>
138   <?php $pending = pending_requests((int)$USER['id']); ?>
139   <?php if ($pending): ?>
140     <div class="pending-requests">
141       <h3>๐Ÿ“ฅ Friend Requests (<?= count($pending) ?>)</h3>
142       <?php foreach ($pending as $req): ?>
143         <div class="pending-row">
144           <?php if ($req['avatar']): ?>
145             <img src="<?= e($req['avatar']) ?>" class="av-sm" alt="">
146           <?php else: ?>
147             <span class="av-sm av-init"><?= strtoupper($req['username'][0]) ?></span>
148           <?php endif; ?>
149           <a href="<?= u('users/profile.php?u='.urlencode($req['username'])) ?>">@<?= e($req['username']) ?></a>
150           <button class="btn-primary btn-sm" onclick="friendAction('accept',<?= $req['id'] ?>,this)">Accept</button>
151           <button class="btn-ghost btn-sm"   onclick="friendAction('decline',<?= $req['id'] ?>,this)">Decline</button>
152         </div>
153       <?php endforeach; ?>
154     </div>
155   <?php endif; ?>
156 <?php endif; ?>
157 
158 <!-- Tabs -->
159 <div class="profile-tabs">
160   <button class="tab-btn active" onclick="showTab('topics',this)">
161     Topics <span class="tab-count"><?= count($topics) ?></span>
162   </button>
163   <button class="tab-btn" onclick="showTab('replies',this)">
164     Replies <span class="tab-count"><?= count($posts) ?></span>
165   </button>
166   <?php if ($showFriends): ?>
167     <button class="tab-btn" onclick="showTab('friends',this)">
168       Friends <span class="tab-count"><?= count($friendsList) ?></span>
169     </button>
170   <?php endif; ?>
171 </div>
172 
173 <!-- Topics tab -->
174 <div id="tab-topics" class="tab-pane">
175   <?php foreach ($topics as $t): ?>
176     <div class="topic-row">
177       <div class="tr-body">
178         <a href="<?= u('forum/topic.php?slug='.urlencode($t['slug'])) ?>" class="tr-title"><?= e($t['title']) ?></a>
179         <div class="tr-meta">
180           <a href="<?= u('forum/category.php?slug='.urlencode($t['cat_slug'])) ?>"
181              class="cat-tag" style="--cc:#3b82f6"><?= e($t['cat_name']) ?></a>
182           <span class="ago" data-ts="<?= e($t['created_at']) ?>"></span>
183         </div>
184       </div>
185       <div class="tr-counts"><span>๐Ÿ’ฌ <?= $t['reply_count'] ?></span></div>
186     </div>
187   <?php endforeach; ?>
188   <?php if (!$topics): ?><p class="empty-msg">No topics yet.</p><?php endif; ?>
189 </div>
190 
191 <!-- Replies tab -->
192 <div id="tab-replies" class="tab-pane hidden">
193   <?php foreach ($posts as $p): ?>
194     <div class="reply-card">
195       <div class="rc-top">
196         <a href="<?= u('forum/topic.php?slug='.urlencode($p['topic_slug']).'#post-'.$p['id']) ?>">
197           โ†ฉ <?= e($p['topic_title']) ?>
198         </a>
199         <span class="ago" data-ts="<?= e($p['created_at']) ?>"></span>
200       </div>
201       <div class="rc-body md" data-raw="<?= e(base64_encode($p['content'])) ?>"><?= e($p['content']) ?></div>
202     </div>
203   <?php endforeach; ?>
204   <?php if (!$posts): ?><p class="empty-msg">No replies yet.</p><?php endif; ?>
205 </div>
206 
207 <!-- Friends tab -->
208 <?php if ($showFriends): ?>
209   <div id="tab-friends" class="tab-pane hidden">
210     <?php if ($friendsList): ?>
211       <div class="friends-grid">
212         <?php foreach ($friendsList as $f): ?>
213           <?php $ft = karma_tier((int)$f['karma']); ?>
214           <a href="<?= u('users/profile.php?u='.urlencode($f['username'])) ?>" class="friend-card">
215             <?php if ($f['avatar']): ?>
216               <img src="<?= e($f['avatar']) ?>" class="av-md" alt="">
217             <?php else: ?>
218               <span class="av-md av-init"><?= strtoupper($f['username'][0]) ?></span>
219             <?php endif; ?>
220             <div class="friend-name">@<?= e($f['username']) ?></div>
221             <div class="friend-karma" style="color:<?= e($ft['color']) ?>"><?= $ft['icon'] ?> <?= number_format((int)$f['karma']) ?></div>
222             <div class="friend-role"><span class="role-tag role-<?= e($f['role']) ?>"><?= e($f['role']) ?></span></div>
223           </a>
224         <?php endforeach; ?>
225       </div>
226     <?php else: ?>
227       <p class="empty-msg">No friends yet.</p>
228     <?php endif; ?>
229   </div>
230 <?php endif; ?>
231 
232 <script>
233 function showTab(name,btn){
234   document.querySelectorAll('.tab-pane').forEach(function(p){p.classList.add('hidden');});
235   document.querySelectorAll('.tab-btn').forEach(function(b){b.classList.remove('active');});
236   document.getElementById('tab-'+name).classList.remove('hidden');
237   btn.classList.add('active');
238 }
239 function friendAction(action,targetId,btn){
240   var fd=new FormData();
241   fd.append('action',action);fd.append('target_id',targetId);fd.append('csrf',NX.csrf);
242   fetch(NX.base+'/api/friend.php',{method:'POST',body:fd})
243     .then(function(r){return r.json();})
244     .then(function(d){
245       if(d.ok){toast(d.msg,'ok');setTimeout(function(){location.reload();},800);}
246       else toast(d.error,'err');
247     });
248 }
249 </script>
250 <?php include __DIR__ . '/../views/partials/layout_end.php'; ?>