150 lines
4.5 KiB
Vue
150 lines
4.5 KiB
Vue
<template>
|
|
<div v-if="isOpen" class="modal-backdrop open" @click.self="closeModal">
|
|
<div class="modal-container" role="dialog" aria-modal="true" aria-labelledby="createListModalTitle">
|
|
<div class="modal-header">
|
|
<h3 id="createListModalTitle">Create New List</h3>
|
|
<button class="close-button" @click="closeModal" aria-label="Close modal">
|
|
<svg class="icon" aria-hidden="true"><use xlink:href="#icon-close" /></svg>
|
|
</button>
|
|
</div>
|
|
<form @submit.prevent="onSubmit">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="listName" class="form-label">List Name</label>
|
|
<input
|
|
type="text"
|
|
id="listName"
|
|
v-model="listName"
|
|
class="form-input"
|
|
required
|
|
ref="listNameInput"
|
|
/>
|
|
<p v-if="formErrors.listName" class="form-error-text">{{ formErrors.listName }}</p>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="description" class="form-label">Description</label>
|
|
<textarea
|
|
id="description"
|
|
v-model="description"
|
|
class="form-input"
|
|
rows="3"
|
|
></textarea>
|
|
</div>
|
|
|
|
<div class="form-group" v-if="groups && groups.length > 0">
|
|
<label for="selectedGroup" class="form-label">Associate with Group (Optional)</label>
|
|
<select id="selectedGroup" v-model="selectedGroupId" class="form-input">
|
|
<option :value="null">None</option>
|
|
<option v-for="group in groups" :key="group.value" :value="group.value">
|
|
{{ group.label }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-neutral" @click="closeModal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary ml-2" :disabled="loading">
|
|
<span v-if="loading" class="spinner-dots-sm" role="status"><span/><span/><span/></span>
|
|
Create
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch, nextTick } from 'vue';
|
|
import { useVModel, onClickOutside } from '@vueuse/core';
|
|
import { apiClient, API_ENDPOINTS } from '@/config/api'; // Assuming this path is correct
|
|
import { useNotificationStore } from '@/stores/notifications';
|
|
|
|
const props = defineProps<{
|
|
modelValue: boolean;
|
|
groups?: Array<{ label: string; value: number }>;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: boolean): void;
|
|
(e: 'created'): void;
|
|
}>();
|
|
|
|
const isOpen = useVModel(props, 'modelValue', emit);
|
|
const listName = ref('');
|
|
const description = ref('');
|
|
const selectedGroupId = ref<number | null>(null); // Store only the ID
|
|
const loading = ref(false);
|
|
const formErrors = ref<{ listName?: string }>({});
|
|
const notificationStore = useNotificationStore();
|
|
|
|
const listNameInput = ref<HTMLInputElement | null>(null);
|
|
const modalContainerRef = ref<HTMLElement | null>(null); // For onClickOutside
|
|
|
|
watch(isOpen, (newVal) => {
|
|
if (newVal) {
|
|
// Reset form when opening
|
|
listName.value = '';
|
|
description.value = '';
|
|
selectedGroupId.value = null;
|
|
formErrors.value = {};
|
|
nextTick(() => {
|
|
listNameInput.value?.focus();
|
|
});
|
|
}
|
|
});
|
|
|
|
onClickOutside(modalContainerRef, () => {
|
|
if (isOpen.value) {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
const closeModal = () => {
|
|
isOpen.value = false;
|
|
};
|
|
|
|
const validateForm = () => {
|
|
formErrors.value = {};
|
|
if (!listName.value.trim()) {
|
|
formErrors.value.listName = 'Name is required';
|
|
}
|
|
return Object.keys(formErrors.value).length === 0;
|
|
};
|
|
|
|
const onSubmit = async () => {
|
|
if (!validateForm()) {
|
|
return;
|
|
}
|
|
loading.value = true;
|
|
try {
|
|
await apiClient.post(API_ENDPOINTS.LISTS.BASE, {
|
|
name: listName.value,
|
|
description: description.value,
|
|
group_id: selectedGroupId.value,
|
|
});
|
|
|
|
notificationStore.addNotification({ message: 'List created successfully', type: 'success' });
|
|
|
|
emit('created');
|
|
closeModal();
|
|
} catch (error: unknown) {
|
|
const message = error instanceof Error ? error.message : 'Failed to create list';
|
|
notificationStore.addNotification({ message, type: 'error' });
|
|
console.error(message, error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.form-error-text {
|
|
color: var(--danger);
|
|
font-size: 0.85rem;
|
|
margin-top: 0.25rem;
|
|
}
|
|
.ml-2 {
|
|
margin-left: 0.5rem; /* from Valerie UI utilities */
|
|
}
|
|
</style> |