xgit simple git

nexus

nexus

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

users/profile.php


1<?php
2require_once __DIR__ . '/../includes/bootstrap.php';
3$uname   = get('u');
4$profile = DB::row('SELECT * FROM users WHERE username=?', [$uname]);
5if (!$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'];
28include __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>
233function 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}
239function 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'; ?>