Add initial implementation of user selector on sudo
This commit is contained in:
parent
21b7b68f50
commit
7b3c1f2b54
5 changed files with 132 additions and 9 deletions
110
ts/components/UserSelector.vue
Normal file
110
ts/components/UserSelector.vue
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<template>
|
||||
<div class="user-selector">
|
||||
<label v-if="label" :for="selectId" class="form-label">
|
||||
{{ label }}
|
||||
</label>
|
||||
|
||||
<select
|
||||
:id="selectId"
|
||||
v-model="selectedUserId"
|
||||
class="form-select"
|
||||
:class="{ 'is-invalid': error }"
|
||||
:disabled="loading || disabled"
|
||||
@change="handleChange"
|
||||
>
|
||||
<option :value="null">{{ placeholder }}</option>
|
||||
<option v-for="user in usersStore.all" :key="user.id" :value="user.id">
|
||||
{{ user.display_name || user.username || `User ${user.id}` }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<div v-if="loading" class="form-text">
|
||||
<span
|
||||
class="spinner-border spinner-border-sm me-2"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
Loading users...
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="invalid-feedback d-block">
|
||||
Failed to load users. Please try again.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed, watch } from "vue";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import type { User } from "@/types";
|
||||
|
||||
interface Props {
|
||||
modelValue?: number | null;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: "update:modelValue", value: number | null): void;
|
||||
(e: "change", user: User | null): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: null,
|
||||
label: "",
|
||||
placeholder: "Select a user...",
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const usersStore = useUserStore();
|
||||
const selectedUserId = ref<number | null>(props.modelValue);
|
||||
const loading = ref(false);
|
||||
const error = ref(false);
|
||||
const selectId = computed(
|
||||
() => `user-select-${Math.random().toString(36).substr(2, 9)}`,
|
||||
);
|
||||
|
||||
// Watch for external changes to modelValue
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
selectedUserId.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
const handleChange = () => {
|
||||
emit("update:modelValue", selectedUserId.value);
|
||||
|
||||
const selectedUser = selectedUserId.value
|
||||
? usersStore.byID(selectedUserId.value)
|
||||
: null;
|
||||
|
||||
emit("change", selectedUser || null);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// Only fetch if users haven't been loaded yet
|
||||
if (!usersStore.all) {
|
||||
loading.value = true;
|
||||
error.value = false;
|
||||
|
||||
try {
|
||||
await usersStore.fetchAll();
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch users:", err);
|
||||
error.value = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-selector {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -4,7 +4,7 @@ import { User } from "../types";
|
|||
import { SSEManager } from "../SSEManager";
|
||||
import { useSessionStore } from "./session";
|
||||
|
||||
export const useUsersStore = defineStore("users", () => {
|
||||
export const useUserStore = defineStore("users", () => {
|
||||
// State
|
||||
const _byID = ref<Map<number, User>>(new Map());
|
||||
const all = ref<User[] | null>(null);
|
||||
|
|
@ -327,7 +327,12 @@
|
|||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="userSearch" class="form-label">Search Users</label>
|
||||
<user-selector></user-selector>
|
||||
<UserSelector
|
||||
v-model="selectedUserId"
|
||||
label="Choose a user"
|
||||
placeholder="Select a user..."
|
||||
@change="onUserChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="userRole" class="form-label">Filter by Role</label>
|
||||
|
|
@ -420,6 +425,14 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useSessionStore } from "@/store/session";
|
||||
import UserSelector from "@/components/UserSelector.vue";
|
||||
import type { User } from "@/types";
|
||||
|
||||
const session = useSessionStore();
|
||||
|
||||
const selectedUserId = ref<number | null>(null);
|
||||
|
||||
const onUserChange = (user: User | null) => {
|
||||
console.log("Selected user:", user);
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useUsersStore } from "@/store/users";
|
||||
import { useUserStore } from "@/store/user";
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
|
|
@ -120,9 +120,9 @@ interface URLConfiguration {
|
|||
// }>()
|
||||
|
||||
// Reactive state
|
||||
const usersStore = useUsersStore();
|
||||
const userStore = useUserStore();
|
||||
const users = computed(() => {
|
||||
return usersStore.all;
|
||||
return userStore.all;
|
||||
});
|
||||
const urlConfiguration = ref<URLConfiguration>({
|
||||
userAdd: "/configuration/user/add", // Update with your actual route
|
||||
|
|
@ -150,7 +150,7 @@ const deactivateUser = (userId: number): void => {
|
|||
// Lifecycle hooks
|
||||
onMounted(() => {
|
||||
// Fetch users from API if needed
|
||||
usersStore.fetchAll();
|
||||
userStore.fetchAll();
|
||||
});
|
||||
|
||||
// Optional: API call example
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ pre {
|
|||
<script setup lang="ts">
|
||||
import { computed, defineComponent, onMounted, ref, reactive } from "vue";
|
||||
import { useSessionStore } from "@/store/session";
|
||||
import { useUsersStore } from "@/store/users";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { User } from "@/types";
|
||||
|
||||
interface Props {
|
||||
|
|
@ -257,7 +257,7 @@ const fileInput = ref<HTMLInputElement | null>(null);
|
|||
const props = defineProps<Props>();
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const selectedTag = ref<string>("");
|
||||
const usersStore = useUsersStore();
|
||||
const userStore = useUserStore();
|
||||
const session = useSessionStore();
|
||||
const user = ref<User | null>(null);
|
||||
|
||||
|
|
@ -390,7 +390,7 @@ const cancelChanges = () => {
|
|||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
usersStore.fetchAll().then((users) => {
|
||||
userStore.fetchAll().then((users) => {
|
||||
for (const u of users) {
|
||||
if (u.id == props.id) {
|
||||
user.value = u;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue