Skip to content

Commit

Permalink
Added user attrs to the UserDialog (#589)
Browse files Browse the repository at this point in the history
* Added user attrs to the UserDialog

* Removed defineComponent

---------

Co-authored-by: Ramin Haeri Azad <[email protected]>
  • Loading branch information
kazoompa and Ramin Haeri Azad authored Jan 13, 2025
1 parent 38c6a1a commit 845306a
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 3 deletions.
5 changes: 3 additions & 2 deletions agate-ui/src/components/SystemUserAttributes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
<q-td key="required" :props="props">
<q-icon :name="props.row.required ? 'check_box' : 'check_box_outline_blank'" size="sm" dense />
</q-td>
</q-tr> </template
>/
</q-tr>
</template>
</q-table>

<confirm-dialog
Expand Down Expand Up @@ -156,6 +156,7 @@ function onCancel() {
}
function onSavedAttribute() {
selected.value = undefined;
showEdit.value = false;
systemStore.init();
}
Expand Down
6 changes: 5 additions & 1 deletion agate-ui/src/components/UserDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,11 @@
</div>
</div>
</q-form>

<user-attributes-list class="q-mt-lg" v-model="selected!.attributes" />
</q-card-section>


<q-separator />

<q-card-actions align="right" class="bg-grey-3">
Expand All @@ -180,9 +183,10 @@
</template>

<script setup lang="ts">
import { copyToClipboard } from 'quasar';
import type { UserDto } from 'src/models/Agate';
import { notifyError, notifyInfo, notifySuccess } from 'src/utils/notify';
import { copyToClipboard } from 'quasar';
import UserAttributesList from 'src/components/attributes/UserAttributesList.vue';
const { t } = useI18n();
const userStore = useUserStore();
Expand Down
103 changes: 103 additions & 0 deletions agate-ui/src/components/attributes/UserAttributeDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<template>
<q-dialog v-model="showDialog" persistent @hide="onHide">
<q-card class="dialog-md">
<q-card-section>
<div class="text-h6">{{ editMode ? t('user.attributes.update') : t('user.attributes.add') }}</div>
</q-card-section>

<q-separator />
<q-card-section>
<q-form ref="formRef">
<q-input
v-model="newAttribue.name"
dense
type="text"
:label="t('name') + ' *'"
class="q-mb-md"
lazy-rules
:rules="[validateRequired, validateUnique]"
:disable="editMode"
>
</q-input>
<q-input
v-model="newAttribue.value"
dense
type="text"
:label="t('value')"
class="q-mb-md"
lazy-rules
/>
</q-form>
</q-card-section>

<q-separator />

<q-card-actions align="right" class="bg-grey-3">
<q-btn flat :label="t('cancel')" color="secondary" @click="onCancel" v-close-popup />
<q-btn flat :label="t('save')" color="primary" @click="onSave" />
</q-card-actions>
</q-card>
</q-dialog>
</template>

<script setup lang="ts">
import type { AttributeDto } from 'src/models/Agate';
interface DialogProps {
modelValue: boolean;
attributes: AttributeDto[];
attribute: AttributeDto | undefined;
}
const { t } = useI18n();
const props = defineProps<DialogProps>();
const emit = defineEmits(['update:modelValue', 'saved', 'cancel']);
const formRef = ref();
const showDialog = ref(props.modelValue);
const newAttribue = ref<AttributeDto>({} as AttributeDto);
const editMode = computed(() => !!props.attribute && !!props.attribute.name);
const attributes = computed<AttributeDto[]>(() => props.attributes || [] as AttributeDto[]);
function validateRequired(value: string) {
return !!value || t('name_required');
}
function validateUnique(value: string) {
if (attributes) {
const exists = attributes.value.find((attr) => attr.name === value);
return !exists || t('user.attributes.name_exists');
}
return true;
}
watch(
() => props.modelValue,
(value) => {
if (value) {
if (props.attribute) {
newAttribue.value = { ...props.attribute } as AttributeDto;
} else {
newAttribue.value = {} as AttributeDto;
}
}
showDialog.value = value;
},
);
function onHide() {
emit('update:modelValue', false);
}
function onCancel() {
emit('cancel');
}
async function onSave() {
const valid = await formRef.value.validate();
if (valid) {
emit('saved', newAttribue.value);
emit('update:modelValue', false);
}
}
</script>
173 changes: 173 additions & 0 deletions agate-ui/src/components/attributes/UserAttributesList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<template>
<div>
<span class="text-h6">
{{ t('user.attributes.title') }}
</span>
<q-table
:rows="filteredAttributes"
flat
row-key="name"
:columns="columns"
:pagination="initialPagination"
:hide-pagination="filteredAttributes.length <= initialPagination.rowsPerPage"
>
<template v-slot:top-left>
<q-btn size="sm" icon="add" color="primary" :label="t('add')" @click="onAdd" />
</template>
<template v-slot:top-right>
<q-input v-model="filter" debounce="300" :placeholder="t('search')" dense clearable class="q-mr-md">
<template v-slot:prepend>
<q-icon name="search" />
</template>
</q-input>
</template>
<template v-slot:body="props">
<q-tr :props="props" @mouseover="onOverRow(props.row)" @mouseleave="onLeaveRow(props.row)">
<q-td key="name" :props="props">
<span class="text-primary">{{ props.row.name }}</span>
<div class="float-right">
<q-btn
rounded
dense
flat
size="sm"
color="secondary"
:icon="toolsVisible[props.row.name] ? 'edit' : 'none'"
:title="t('edit')"
class="q-ml-xs"
@click="onShowEdit(props.row)"
/>
<q-btn
rounded
dense
flat
size="sm"
color="secondary"
:title="t('delete')"
:icon="toolsVisible[props.row.name] ? 'delete' : 'none'"
class="q-ml-xs"
@click="onShowDelete(props.row)"
/>
</div>
</q-td>
<q-td key="value" :props="props">
<span>{{ props.row.value }}</span>
</q-td>
</q-tr>
</template>
</q-table>

<confirm-dialog
v-model="showDelete"
:title="t('user.attributes.remove')"
:text="t('user.attributes.remove_confirm', { name: selected?.name })"
@confirm="onDelete"
/>

<user-attribute-dialog
v-model="showEdit"
:attributes="userAttributes"
:attribute="selected"
@saved="onSavedAttribute"
@cancel="onCancel"
/>
</div>
</template>

<script setup lang="ts">
import type { AttributeDto } from 'src/models/Agate';
import { DefaultAlignment } from 'src/components/models';
import ConfirmDialog from 'src/components/ConfirmDialog.vue';
import UserAttributeDialog from 'src/components/attributes/UserAttributeDialog.vue';
interface Props {
modelValue: AttributeDto[] | undefined;
}
const props = defineProps<Props>();
const emit = defineEmits(['update:modelValue']);
const { t } = useI18n();
const userAttributes = computed({
get: () => props.modelValue ?? ([] as AttributeDto[]),
set: (value: AttributeDto[]) => {
emit('update:modelValue', value);
},
});
const toolsVisible = ref<{ [key: string]: boolean }>({});
const initialPagination = ref({
descending: false,
page: 1,
rowsPerPage: 5,
});
const filteredAttributes = computed(
() =>
userAttributes.value.filter((attr) =>
filter.value ? attr.name.toLowerCase().includes(filter.value.toLowerCase()) : true,
) || [],
);
const filter = ref('');
const selected = ref();
const showEdit = ref(false);
const showDelete = ref(false);
const columns = computed(() => [
{ name: 'name', label: t('name'), field: 'name', align: DefaultAlignment },
{ name: 'value', label: t('value'), field: 'value', align: DefaultAlignment },
]);
function onOverRow(row: AttributeDto) {
toolsVisible.value[row.name] = true;
}
function onLeaveRow(row: AttributeDto) {
toolsVisible.value[row.name] = false;
}
function onAdd() {
selected.value = undefined;
showEdit.value = true;
}
function onShowEdit(row: AttributeDto) {
selected.value = row;
showEdit.value = true;
}
function onShowDelete(row: AttributeDto) {
selected.value = row;
showDelete.value = true;
}
function onDelete() {
if (selected.value) {
const index = userAttributes.value.indexOf(selected.value);
if (index !== -1) {
userAttributes.value.splice(index, 1);
emit('update:modelValue', userAttributes.value)
}
}
}
function onCancel() {
showEdit.value = false;
}
function onSavedAttribute(newAttribute: AttributeDto) {
if (newAttribute) {
const index = userAttributes.value.findIndex((attr) => attr.name === newAttribute.name);
if (index !== -1) {
userAttributes.value[index] = newAttribute;
} else {
userAttributes.value.push(newAttribute);
}
emit('update:modelValue', userAttributes.value);
}
selected.value = undefined;
showEdit.value = false;
}
</script>
12 changes: 12 additions & 0 deletions agate-ui/src/i18n/en/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ export default {
PENDING: 'User is pending approval.',
},
update_password: 'Update password',
attributes: {
title: 'User Attributes',
add: 'Add User Attribute',
update: 'Update User Attribute',
updated: 'User attribute updated successfully',
update_failed: 'Failed to update user attribute',
remove: 'Remove User Attribute',
remove_confirm: 'Please confirm the removal of the user attribute: {name}',
name_exists: 'User attribute name already exists',
}
},
realm: {
activate: 'Activate',
Expand Down Expand Up @@ -240,6 +250,8 @@ export default {
values_hint: 'Comma separated values',
add: 'Add Attribute',
update: 'Update Attribute',
updated: 'Attribute updated successfully',
update_failed: 'Failed to update attribute',
remove: 'Remove Attribute',
remove_confirm: 'Please confirm the removal of the attribute: {name}',
name_exists: 'Attribute name already exists',
Expand Down
12 changes: 12 additions & 0 deletions agate-ui/src/i18n/fr/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ export default {
PENDING: "Usager en attente d'approbation.",
},
update_password: 'Mettre à jour le mot de passe',
attributes: {
title: 'Attributs utilisateur',
add: 'Ajouter un attribut d\'utilisateur',
update: "Mettre à Jour l'attribut d'utilisateur",
updated: 'Attribut utilisateur mis à jour avec succès',
update_failed: 'Échec de la mise à jour de l\'attribut utilisateur',
remove: "Supprimer l'attribut utilisateur",
remove_confirm: "Veuillez confirmer la suppression de l'attribut utilisateur : {name}",
name_exists: 'Le nom de l\'attribut utilisateur existe déjà',
}
},
realm: {
activate: 'Activer',
Expand Down Expand Up @@ -240,6 +250,8 @@ export default {
values_hint: 'Valeurs séparées par des virgules',
add: 'Ajouter un Attribut',
update: "Mettre à Jour l'Attribut",
updated: 'Attribut mis à jour',
update_failed: 'Échec de la mise à jour de l\'attribut',
remove: "Supprimer l'Attribut",
remove_confirm: "Veuillez confirmer la suppression de l'attribut : {name}",
name_exists: "Le nom de l'attribut existe déjà",
Expand Down

0 comments on commit 845306a

Please sign in to comment.