summaryrefslogtreecommitdiff
path: root/garden/src/pages/council/users.tsx
blob: e88ad85963fd2961cfb3d954a9fb359ac4e6b7bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { createSignal, onMount, Show, For } from "solid-js";
import { useNavigate, useSearchParams } from "@solidjs/router";
import { council } from "../../store/council";
import { ROLE_LABELS } from "../../types/roles";
import type { AdminUser } from "../../types/admin";
import StaffGuard from "../../components/StaffGuard";

export default function CouncilUsers() {
  const navigate = useNavigate();
  const [searchParams, setSearchParams] = useSearchParams();
  const [searchInput, setSearchInput] = createSignal("");

  onMount(() => {
    const p = parseInt(searchParams.page as string) || 1;
    council.loadUsers(p, council.search());
  });

  function handleSearch(event: Event) {
    event.preventDefault();
    council.setSearch(searchInput());
    setSearchParams({ page: "1" });
    council.loadUsers(1, searchInput());
  }

  function goToPage(p: number) {
    setSearchParams({ page: String(p) });
    council.loadUsers(p, council.search());
  }

  function statusBadge(user: AdminUser) {
    if (user.account_banned) return "banned";
    if (user.account_disabled) return "disabled";
    if (!user.email_verified) return "unverified";
    return "active";
  }

  function formatDate(date: string) {
    return new Date(date).toLocaleDateString();
  }

  function sortIndicator(field: string) {
    if (council.sortField() !== field) return "";
    return council.sortOrder() === "asc" ? " \u25B2" : " \u25BC";
  }

  return (
    <StaffGuard>
    <section>
      <h2 class="page-title">Users</h2>

      <form class="council-search" onSubmit={handleSearch}>
        <input
          type="text"
          placeholder="Search by username, display name, or email..."
          value={searchInput()}
          onInput={(e) => setSearchInput(e.currentTarget.value)}
        />
        <button type="submit" class="form-button">Search</button>
      </form>

      <div class="council-grid">
        <div class="council-grid-header">
          <span class="council-sortable" onClick={() => council.toggleSort("display_name")}>User{sortIndicator("display_name")}</span>
          <span class="council-sortable" onClick={() => council.toggleSort("email")}>Email{sortIndicator("email")}</span>
          <span class="council-sortable" onClick={() => council.toggleSort("role")}>Role{sortIndicator("role")}</span>
          <span>Status</span>
          <span class="council-sortable" onClick={() => council.toggleSort("created_at")}>Joined{sortIndicator("created_at")}</span>
        </div>
        <Show when={!council.loading()} fallback={
          <div class="council-grid-empty">Loading...</div>
        }>
          <Show when={council.users().length} fallback={
            <div class="council-grid-empty">No users found.</div>
          }>
            <For each={council.users()}>
              {(user: AdminUser) => (
                <div class="council-grid-row" onClick={() => navigate(`/council/users/${user.username}`)}>
                  <span class="council-user-cell">
                    <img src={user.avatar_url} alt="" class="council-avatar" />
                    <span>
                      <span class="council-display-name">{user.display_name}</span>
                      <span class="council-username">@{user.username}</span>
                    </span>
                  </span>
                  <span>{user.email}</span>
                  <span>
                    <span class={`council-role council-role-${user.role}`}>{ROLE_LABELS[user.role] || user.role}</span>
                  </span>
                  <span>
                    <span class={`council-status council-status-${statusBadge(user)}`}>
                      {statusBadge(user)}
                    </span>
                  </span>
                  <span>{formatDate(user.created_at)}</span>
                </div>
              )}
            </For>
          </Show>
        </Show>
      </div>

      <Show when={council.totalPages() > 1}>
        <div class="council-pagination">
          <button
            class="council-page-btn"
            disabled={council.page() <= 1}
            onClick={() => goToPage(council.page() - 1)}
          >
            Prev
          </button>
          <span class="council-page-info">
            Page {council.page()} of {council.totalPages()} ({council.total()} users)
          </span>
          <button
            class="council-page-btn"
            disabled={council.page() >= council.totalPages()}
            onClick={() => goToPage(council.page() + 1)}
          >
            Next
          </button>
        </div>
      </Show>
    </section>
    </StaffGuard>
  );
}