forked from verdnatura/salix-front
refs 6105 claimNotes and VnNotes created
This commit is contained in:
parent
96cfea8a41
commit
ee7c2389b5
|
@ -0,0 +1,36 @@
|
|||
<script setup>
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
const $props = defineProps({
|
||||
worker: { type: Number, required: true },
|
||||
description: { type: String, default: null },
|
||||
});
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
</script>
|
||||
<template>
|
||||
<div class="avatar-picture">
|
||||
<QAvatar color="orange">
|
||||
<QImg
|
||||
:src="`/api/Images/user/160x160/${$props.worker}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</QAvatar>
|
||||
<div class="description">
|
||||
<slot name="description">
|
||||
<p>
|
||||
{{ $props.description }}
|
||||
</p>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.avatar-picture {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,156 @@
|
|||
<script setup>
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import AvatarPicture from 'src/components/ui/AvatarPicture.vue';
|
||||
import { toDateHour } from 'src/filters';
|
||||
import { ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const $props = defineProps({
|
||||
id: { type: String, required: true },
|
||||
url: { type: String, default: null },
|
||||
filter: { type: Object, default: () => {} },
|
||||
body: { type: Object, default: () => {} },
|
||||
addNote: { type: Boolean, default: false },
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const notes = ref([]);
|
||||
const noteModal = ref(false);
|
||||
const newNote = ref('');
|
||||
const claimObservationRef = ref();
|
||||
|
||||
function setNotes(data) {
|
||||
notes.value = data;
|
||||
}
|
||||
|
||||
async function fetch() {
|
||||
const body = $props.body;
|
||||
Object.assign(body, { text: newNote.value });
|
||||
await axios.post($props.url, body);
|
||||
claimObservationRef.value.fetch();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
:url="$props.url"
|
||||
:filter="$props.filter"
|
||||
@on-fetch="setNotes"
|
||||
auto-load
|
||||
ref="claimObservationRef"
|
||||
/>
|
||||
<div class="notes" ref="notesContainer">
|
||||
<QDialog v-model="noteModal" persistent>
|
||||
<QCard class="note-dialog q-pa-sm">
|
||||
<QCardSection class="note-dialog__header">
|
||||
<div class="note-dialog__title">
|
||||
<QIcon name="draft" class="note-dialog__title-icon" />
|
||||
<div class="text-h6 note-dialog__title-text">
|
||||
{{ t('Add note') }}
|
||||
</div>
|
||||
</div>
|
||||
<QBtn icon="close" flat round dense v-close-popup />
|
||||
</QCardSection>
|
||||
<QCardSection class="note-dialog__content">
|
||||
<QInput
|
||||
autofocus
|
||||
type="textarea"
|
||||
:hint="t('Add note here...')"
|
||||
filled
|
||||
autogrow
|
||||
v-model="newNote"
|
||||
></QInput>
|
||||
</QCardSection>
|
||||
<QCardActions class="note-dialog__actions q-mr-md">
|
||||
<QBtn
|
||||
flat
|
||||
:label="t('globals.close')"
|
||||
color="primary"
|
||||
v-close-popup
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.save')"
|
||||
color="primary"
|
||||
v-close-popup
|
||||
@click="fetch"
|
||||
/>
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
<QCard class="q-pa-md" v-for="(note, index) in notes" :key="index">
|
||||
<div class="picture q-pa-sm">
|
||||
<slot name="picture">
|
||||
<AvatarPicture :worker="note.workerFk">
|
||||
<template #description>
|
||||
<span class="link">
|
||||
{{ `${note.worker.firstName} ${note.worker.lastName}` }}
|
||||
</span>
|
||||
<WorkerDescriptorProxy :id="note.worker.id" />
|
||||
</template>
|
||||
</AvatarPicture>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="text">
|
||||
<slot name="text">
|
||||
{{ note.text }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<slot name="actions">
|
||||
<div>
|
||||
{{ toDateHour(note.created) }}
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</QCard>
|
||||
<QBtn
|
||||
v-if="addNote"
|
||||
class="add-btn"
|
||||
color="primary"
|
||||
round
|
||||
@click="noteModal = true"
|
||||
>
|
||||
<QIcon name="add" size="md"></QIcon>
|
||||
</QBtn>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.q-card {
|
||||
min-width: 350px;
|
||||
}
|
||||
.note-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.note-dialog__header {
|
||||
width: 100%;
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.note-dialog__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
color: $primary;
|
||||
font-size: large;
|
||||
}
|
||||
.note-dialog__content {
|
||||
width: 95%;
|
||||
}
|
||||
.note-dialog__actions {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
.add-btn {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
position: sticky;
|
||||
left: 95%;
|
||||
bottom: 2%;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
es:
|
||||
Add note here...: Añadir nota aquí...
|
||||
Add note: Añadir nota
|
||||
</i18n>
|
|
@ -1,6 +1,7 @@
|
|||
import toLowerCase from './toLowerCase';
|
||||
import toDate from './toDate';
|
||||
import toDateString from './toDateString';
|
||||
import toDateHour from './toDateHour';
|
||||
import toCurrency from './toCurrency';
|
||||
import toPercentage from './toPercentage';
|
||||
import toLowerCamel from './toLowerCamel';
|
||||
|
@ -11,6 +12,7 @@ export {
|
|||
toLowerCamel,
|
||||
toDate,
|
||||
toDateString,
|
||||
toDateHour,
|
||||
toCurrency,
|
||||
toPercentage,
|
||||
dashIfEmpty,
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export default function toDateHour(date) {
|
||||
const dateHour = new Date(date).toLocaleDateString('es-ES', {
|
||||
timeZone: 'Europe/Madrid',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
return dateHour;
|
||||
}
|
|
@ -3,11 +3,12 @@ import LeftMenu from 'components/LeftMenu.vue';
|
|||
import { getUrl } from 'composables/getUrl';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { computed, onMounted } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import ClaimDescriptor from './ClaimDescriptor.vue';
|
||||
|
||||
import { onMounted } from 'vue';
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
@ -21,11 +22,6 @@ const $props = defineProps({
|
|||
const entityId = computed(() => {
|
||||
return $props.id || route.params.id;
|
||||
});
|
||||
const claimSections = [
|
||||
{ name: 'Notes', url: '/note/index', icon: 'draft' },
|
||||
{ name: 'Development', url: '/development', icon: 'vn:traceability' },
|
||||
{ name: 'Action', url: '/action', icon: 'vn:actions' },
|
||||
];
|
||||
|
||||
let salixUrl;
|
||||
onMounted(async () => {
|
||||
|
@ -49,17 +45,35 @@ onMounted(async () => {
|
|||
<QSeparator />
|
||||
<QList>
|
||||
<QItem
|
||||
v-for="section in claimSections"
|
||||
:key="section.name"
|
||||
active-class="text-primary"
|
||||
:href="salixUrl + section.url"
|
||||
:to="`/claim/${entityId}/notes`"
|
||||
clickable
|
||||
v-ripple
|
||||
>
|
||||
<QItemSection avatar>
|
||||
<QIcon :name="section.icon" />
|
||||
<QIcon name="draft" />
|
||||
</QItemSection>
|
||||
<QItemSection> {{ t(section.name) }} </QItemSection>
|
||||
<QItemSection> {{ t('Notes') }} </QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
active-class="text-primary"
|
||||
clickable
|
||||
v-ripple
|
||||
:href="`${salixUrl}/development`"
|
||||
>
|
||||
<QItemSection avatar>
|
||||
<QIcon name="vn:traceability"></QIcon>
|
||||
</QItemSection>
|
||||
<QItemSection>{{ t('Development') }}</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
active-class="text-primary"
|
||||
clickable
|
||||
v-ripple
|
||||
:href="`${salixUrl}/action`"
|
||||
>
|
||||
<QItemSection avatar><QIcon name="vn:actions"></QIcon></QItemSection>
|
||||
<QItemSection>{{ t('Action') }}</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QScrollArea>
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<script setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import VnNotes from 'src/components/ui/VnNotes.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
const id = route.params.id;
|
||||
|
||||
const claimFilter = {
|
||||
order: 'created DESC',
|
||||
where: { claimFk: id },
|
||||
fields: ['created', 'workerFk', 'text'],
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id', 'firstName', 'lastName'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const body = {
|
||||
claimFk: id,
|
||||
workerFk: user.value.id,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="claim-notes">
|
||||
<VnNotes
|
||||
:add-note="true"
|
||||
:id="id"
|
||||
url="claimObservations"
|
||||
:filter="claimFilter"
|
||||
:body="body"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.q-card {
|
||||
width: 100%;
|
||||
max-width: 70em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.claim-notes {
|
||||
.notes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
> * {
|
||||
margin: 10px;
|
||||
}
|
||||
.text {
|
||||
flex: 70%;
|
||||
padding: 10px;
|
||||
}
|
||||
.picture {
|
||||
flex: 15%;
|
||||
.avatar-picture {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
flex: 15%;
|
||||
align-self: baseline;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 1150px) {
|
||||
.claim-notes {
|
||||
.text {
|
||||
margin-top: 20px;
|
||||
order: 3;
|
||||
flex: 100%;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: end;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -103,6 +103,15 @@ export default {
|
|||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimLog.vue'),
|
||||
},
|
||||
{
|
||||
name: 'ClaimNotes',
|
||||
path: 'notes',
|
||||
meta: {
|
||||
title: 'notes',
|
||||
icon: 'vn:details',
|
||||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('ClaimNotes', () => {
|
||||
beforeEach(() => {
|
||||
const claimId = 2;
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/claim/${claimId}/notes`);
|
||||
});
|
||||
|
||||
it('should add a new note', () => {
|
||||
const message = 'This is a new message.';
|
||||
cy.get('.add-btn').click();
|
||||
cy.get('.note-dialog__content').type(message);
|
||||
cy.get('.note-dialog__actions .q-btn:nth-child(2)').click();
|
||||
cy.get('.notes > :nth-child(1) > .text').should('have.text', message);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue