Skip to content

Commit

Permalink
Merge pull request #1135 from solliancenet/ah-ui-message-file-attachm…
Browse files Browse the repository at this point in the history
…ent-association

Added visual association to file attachments UI
  • Loading branch information
ciprianjichici authored Jun 13, 2024
2 parents 2d8dd33 + 8ed4ca0 commit d4e80e8
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 25 deletions.
52 changes: 50 additions & 2 deletions src/ui/UserPortal/components/ChatInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,34 @@
<i class="pi pi-info-circle tooltip-component" v-tooltip="'Use Shift+Enter to add a new line'"></i>
<Button
icon="pi pi-paperclip"
label=""
class="file-upload-button"
style="height: 100%;"
@click="showFileUploadDialog = true"
@click="toggleFileAttachmentOverlay"
:badge="$appStore.attachments.length.toString() || null"
/>
<OverlayPanel ref="fileAttachmentPanel">
<div class="attached-files-container">
<h2 style="margin-bottom: 0px;">Attached Files</h2>
<div class="attached-files" v-for="file in $appStore.attachments" v-if="$appStore.attachments.length">
<div class="file-name">{{ file.fileName }}</div>
<div class="file-remove">
<Button icon="pi pi-times" severity="danger" text rounded aria-label="Remove attachment" @click="removeAttachment(file)" />
</div>
</div>
<div v-else>
No files attached
</div>
</div>
<div class="p-d-flex p-jc-end">
<Button
label="Upload File"
icon="pi pi-upload"
class="p-button-primary"
@click="showFileUploadDialog = true"
/>
</div>
</OverlayPanel>
<Dialog v-model:visible="showFileUploadDialog" header="Upload File" modal>
<FileUpload
accept="audio/mpeg,audio/wav"
Expand Down Expand Up @@ -167,7 +191,15 @@ export default {
} catch (error) {
this.$toast.add({ severity: 'error', summary: 'Error', detail: `File upload failed. ${error.message}` });
}
}
},
toggleFileAttachmentOverlay(event: any) {
this.$refs.fileAttachmentPanel.toggle(event);
},
removeAttachment(file: any) {
this.$appStore.attachments = this.$appStore.attachments.filter((f) => f !== file);
},
},
};
</script>
Expand Down Expand Up @@ -260,6 +292,22 @@ export default {
.file-upload-button {
height: 100%;
}
.attached-files-container {
padding-bottom: 1rem;
}
.attached-files {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
flex-wrap: nowrap;
}
.file-remove {
margin-left: 1rem;
}
</style>

<style lang="scss">
Expand Down
49 changes: 30 additions & 19 deletions src/ui/UserPortal/js/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ export default {

try {
const response = await $fetch(`${this.apiUrl}${url}`, options);
return response;
if (response.status >= 400) {
throw response;
}
return response;
} catch (error) {
// If the error is an HTTP error, extract the message directly.
if (error.data) {
throw new Error(error.data.message || error.data || 'Unknown error occurred');
}
throw error;
const errorMessage = formatError(error);
throw new Error(errorMessage);
}
},

Expand Down Expand Up @@ -201,19 +202,29 @@ export default {
* @returns The ObjectID of the uploaded attachment.
*/
async uploadAttachment(file: FormData) {
try {
const response = await this.fetch('/attachments/upload', {
method: 'POST',
body: file,
});

if (response.error || response.status >= 400) {
throw new Error(response.message || 'Unknown error occurred');
}

return response;
} catch (error) {
throw error;
}
const response = await this.fetch('/attachments/upload', {
method: 'POST',
body: file,
});

return response;
},
};

function formatError(error: any): string {
if (error.errors || error.data?.errors) {
const errors = error.errors || error.data.errors;
// Flatten the error messages and join them into a single string
return Object.values(errors).flat().join(' ');
}
if (error.data) {
return error.data.message || error.data || 'An unknown error occurred';
}
if (error.message) {
return error.message;
}
if (typeof error === 'string') {
return error;
}
return 'An unknown error occurred';
}
5 changes: 5 additions & 0 deletions src/ui/UserPortal/js/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,8 @@ export interface OrchestrationRequest {
settings?: OrchestrationSettings;
attachments?: string[];
}

export interface Attachment {
id: string;
fileName: string;
}
7 changes: 7 additions & 0 deletions src/ui/UserPortal/plugins/primevue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import Divider from 'primevue/divider';
import Dropdown from 'primevue/dropdown';
import Avatar from 'primevue/avatar';
import FileUpload from 'primevue/fileupload';
import OverlayPanel from 'primevue/overlaypanel';
import Badge from 'primevue/badge';
import BadgeDirective from 'primevue/badgedirective';


import { defineNuxtPlugin } from '#app';

Expand All @@ -28,6 +32,9 @@ export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('Dropdown', Dropdown);
nuxtApp.vueApp.component('Avatar', Avatar);
nuxtApp.vueApp.component('FileUpload', FileUpload);
nuxtApp.vueApp.component('OverlayPanel', OverlayPanel);
nuxtApp.vueApp.component('Badge', Badge);
nuxtApp.vueApp.directive('badge', BadgeDirective);

nuxtApp.vueApp.use(ToastService);
nuxtApp.vueApp.directive('tooltip', Tooltip);
Expand Down
9 changes: 5 additions & 4 deletions src/ui/UserPortal/stores/appStore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineStore } from 'pinia';
import { useAppConfigStore } from './appConfigStore';
import { useAuthStore } from './authStore';
import type { Session, Message, Agent, ResourceProviderGetResult } from '@/js/types';
import type { Session, Message, Agent, ResourceProviderGetResult, Attachment } from '@/js/types';
import api from '@/js/api';

export const useAppStore = defineStore('app', {
Expand All @@ -13,7 +13,7 @@ export const useAppStore = defineStore('app', {
agents: [] as ResourceProviderGetResult<Agent>[],
selectedAgents: new Map(),
lastSelectedAgent: null as ResourceProviderGetResult<Agent> | null,
attachments: [] as String[],
attachments: [] as Attachment[],
}),

getters: {},
Expand Down Expand Up @@ -170,7 +170,7 @@ export const useAppStore = defineStore('app', {
this.currentSession!.id,
text,
this.getSessionAgent(this.currentSession!).resource,
[...this.attachments.map(String)], // Convert attachments to an array of strings
this.attachments.map(attachment => String(attachment.id)), // Convert attachments to an array of strings
);
await this.getMessages();

Expand Down Expand Up @@ -228,9 +228,10 @@ export const useAppStore = defineStore('app', {
async uploadAttachment(file: FormData) {
try {
const id = await api.uploadAttachment(file);
const fileName = file.get('file')?.name;
// this.attachments.push(id);
// For now, we want to just replace the attachments with the new one.
this.attachments = [id as string];
this.attachments = [{ id, fileName}];
return id;
} catch (error) {
throw error;
Expand Down

0 comments on commit d4e80e8

Please sign in to comment.