Query for users to populate the users page

This commit is contained in:
Eli Ribble 2026-03-28 14:45:49 -07:00
parent 1f9f1ae166
commit e59794f5e0
No known key found for this signature in database
4 changed files with 120 additions and 43 deletions

View file

@ -16,6 +16,7 @@ type contentURLAPI struct {
ReviewTask string `json:"review_task"`
Signal string `json:"signal"`
Upload string `json:"upload"`
Users string `json:"users"`
}
type contentURLs struct {
API contentURLAPI `json:"api"`
@ -48,6 +49,7 @@ func getUserSelf(ctx context.Context, r *http.Request, user platform.User, query
ReviewTask: config.MakeURLNidus("/api/review-task"),
Signal: config.MakeURLNidus("/api/signal"),
Upload: config.MakeURLNidus("/api/upload"),
Users: config.MakeURLNidus("/api/user"),
},
Tegola: urls.Tegola,
Tile: config.MakeURLNidus("/api/tile/{z}/{y}/{x}"),
@ -56,20 +58,30 @@ func getUserSelf(ctx context.Context, r *http.Request, user platform.User, query
}
type responseListUser struct {
Users []platform.User `json:"users"`
Users []*platform.User `json:"users"`
}
func listUser(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*responseListUser, *nhttp.ErrorWithStatus) {
users, err := platform.UsersByOrg(ctx, user.Organization)
if err != nil {
return nil, nhttp.NewError("list users: %w", err)
}
results := make([]*platform.User, len(users))
i := 0
for _, v := range users {
results[i] = v
i++
}
return &responseListUser{
Users: []platform.User{},
Users: results,
}, nil
}
type responseListUserSuggestion struct {
Users []platform.User `json:"users"`
Users []*platform.User `json:"users"`
}
func listUserSuggestion(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*responseListUser, *nhttp.ErrorWithStatus) {
func listUserSuggestion(ctx context.Context, r *http.Request, user platform.User, query queryParams) (*responseListUserSuggestion, *nhttp.ErrorWithStatus) {
if query.Query == nil {
return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "you need to include a query")
}
@ -77,7 +89,7 @@ func listUserSuggestion(ctx context.Context, r *http.Request, user platform.User
if err != nil {
return nil, nhttp.NewError("query suggestions: %w", err)
}
return &responseListUser{
return &responseListUserSuggestion{
Users: users,
}, nil
}

View file

@ -114,7 +114,7 @@ func UsersByOrg(ctx context.Context, org Organization) (map[int32]*User, error)
}
return results, nil
}
func UserSuggestion(ctx context.Context, user User, query string) ([]User, error) {
func UserSuggestion(ctx context.Context, user User, query string) ([]*User, error) {
query_arg := "%" + query + "%"
if user.HasRoot() {
return userSuggestionRoot(ctx, user, query_arg)
@ -122,7 +122,7 @@ func UserSuggestion(ctx context.Context, user User, query string) ([]User, error
return userSuggestionNonRoot(ctx, user, query_arg)
}
}
func userSuggestionNonRoot(ctx context.Context, user User, query_arg string) ([]User, error) {
func userSuggestionNonRoot(ctx context.Context, user User, query_arg string) ([]*User, error) {
users, err := models.Users.Query(
sm.Where(
psql.Or(
@ -137,13 +137,14 @@ func userSuggestionNonRoot(ctx context.Context, user User, query_arg string) ([]
if err != nil {
return nil, fmt.Errorf("query users: %w", err)
}
results := make([]User, len(users))
results := make([]*User, len(users))
for i, user := range users {
results[i] = toUser(user)
u := toUser(user)
results[i] = &u
}
return results, nil
}
func userSuggestionRoot(ctx context.Context, user User, query_arg string) ([]User, error) {
func userSuggestionRoot(ctx context.Context, user User, query_arg string) ([]*User, error) {
users, err := models.Users.Query(
sm.Where(
psql.Or(
@ -171,14 +172,14 @@ func userSuggestionRoot(ctx context.Context, user User, query_arg string) ([]Use
for _, org := range orgs {
org_map[org.ID] = org
}
results := make([]User, len(users))
results := make([]*User, len(users))
for i, user := range users {
u := toUser(user)
org := org_map[user.OrganizationID]
u.Organization = Organization{
model: org,
}
results[i] = u
results[i] = &u
}
return results, nil
}

83
ts/store/users.ts Normal file
View file

@ -0,0 +1,83 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { User } from "../types";
import { SSEManager } from "../SSEManager";
import { useUserStore } from "./user";
export const useUsersStore = defineStore("users", () => {
// State
const _byID = ref<Map<int, User>>(new Map());
const all = ref<User[] | null>(null);
const loading = ref(false);
const error = ref(null);
// Subscription
SSEManager.subscribe("*", (e) => {
if (e.resource.startsWith("users")) {
fetchAll();
}
});
// Actions
function byID(id: int) {
return _byID.value.get(id);
}
async function fetchAll() {
const userStore = useUserStore();
if (userStore.urls == null) {
throw new Error("can't fetch without user URL data");
}
loading.value = true;
error.value = null;
try {
const params = new URLSearchParams();
params.append("sort", "-created");
//if (typeFilter.value) params.append("type", typeFilter.value);
const response = await fetch(`${userStore.urls.api.users}?${params}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
all.value = data.users;
for (const u of data.users) {
_byID.value.set(u.id, u);
}
} catch (err) {
console.error("Error loading users:", err);
throw err;
}
}
async function fetchOne(id: int) {
const userStore = useUserStore();
if (userStore.urls == null) {
throw new Error("can't fetch without user URL data");
}
loading.value = true;
error.value = null;
try {
const response = await fetch(`${userStore.urls.api.user}/${id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
_byID.value.set(data.id, data);
return data;
} catch (err) {
console.error("Error loading users:", err);
throw err;
}
}
return {
// State
all,
// Actions
byID,
fetchAll,
fetchOne,
};
});

View file

@ -1,4 +1,4 @@
<style scoped>
<style scoped lang="scss">
.tech-photo {
width: 48px;
height: 48px;
@ -41,7 +41,7 @@
<div class="card">
<div class="card-body">
<div class="table-responsive">
<div v-if="users" class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-light">
<tr>
@ -100,13 +100,17 @@
</tbody>
</table>
</div>
<div v-else>
<p>loading...</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { computed, onMounted, ref } from "vue";
import { useUsersStore } from "@/store/users";
interface User {
id: number;
@ -127,33 +131,10 @@ interface URLConfiguration {
// }>()
// Reactive state
const users = ref<User[]>([
{
id: 1,
name: "John Davis",
avatar: "https://randomuser.me/api/portraits/men/32.jpg",
role: "Tech I",
status: "Active",
tags: [],
},
{
id: 2,
name: "Sarah Johnson",
avatar: "https://randomuser.me/api/portraits/women/65.jpg",
role: "Tech III",
status: "Active",
tags: ["warrant service", "drone pilot"],
},
{
id: 3,
name: "Michael Chen",
avatar: "https://randomuser.me/api/portraits/men/44.jpg",
role: "Tech I",
status: "Active",
tags: ["drone pilot"],
},
]);
const usersStore = useUsersStore();
const users = computed(() => {
return usersStore.all;
});
const urlConfiguration = ref<URLConfiguration>({
userAdd: "/configuration/user/add", // Update with your actual route
});
@ -181,7 +162,7 @@ const deactivateUser = (userId: number): void => {
// Lifecycle hooks
onMounted(() => {
// Fetch users from API if needed
// fetchUsers()
usersStore.fetchAll();
});
// Optional: API call example