clean up frontend
This commit is contained in:
9
app/components/chat/new-conversation.hbs
Normal file
9
app/components/chat/new-conversation.hbs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<AuButton
|
||||||
|
@icon="add"
|
||||||
|
@size={{if @large "large"}}
|
||||||
|
@loading={{this.create.isRunning}}
|
||||||
|
...attributes
|
||||||
|
{{on "click" (perform this.create)}}
|
||||||
|
>
|
||||||
|
Nieuw gesprek
|
||||||
|
</AuButton>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import Controller from '@ember/controller';
|
import Component from '@glimmer/component';
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import { task } from 'ember-concurrency';
|
import { task } from 'ember-concurrency';
|
||||||
|
|
||||||
export default class ConversationsController extends Controller {
|
export default class ChatNewConversation extends Component {
|
||||||
@service store;
|
@service store;
|
||||||
@service router;
|
@service router;
|
||||||
@service currentSession;
|
@service currentSession;
|
||||||
29
app/components/chat/sidebar.hbs
Normal file
29
app/components/chat/sidebar.hbs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<div class="au-c-sidebar">
|
||||||
|
<div class="au-c-sidebar__header">
|
||||||
|
<AuHeading @level="2" @skin="3">Gesprekken</AuHeading>
|
||||||
|
</div>
|
||||||
|
<div class="au-c-sidebar__content">
|
||||||
|
<ul class="au-c-list-navigation chat-sidebar__list">
|
||||||
|
{{#each @conversations as |conversation|}}
|
||||||
|
<li class="au-c-list-navigation__item">
|
||||||
|
<AuNavigationLink
|
||||||
|
@route="conversation"
|
||||||
|
@model={{conversation.id}}
|
||||||
|
class="chat-sidebar__link"
|
||||||
|
>
|
||||||
|
<span class="chat-sidebar__title">
|
||||||
|
{{if conversation.title conversation.title "Nieuw gesprek"}}
|
||||||
|
</span>
|
||||||
|
<span class="chat-sidebar__meta">
|
||||||
|
{{format-datetime conversation.lastActivity}}
|
||||||
|
</span>
|
||||||
|
</AuNavigationLink>
|
||||||
|
</li>
|
||||||
|
{{else}}
|
||||||
|
<li class="au-c-list-navigation__item">
|
||||||
|
<p class="au-u-muted chat-sidebar__empty">Nog geen gesprekken.</p>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
{{! The list, the composer, the poll loop }}
|
{{! The list, the composer, the poll loop }}
|
||||||
<div class="chat-window">
|
<div class="chat-window">
|
||||||
<div class="chat-window__messages">
|
<div
|
||||||
|
class="chat-window__messages"
|
||||||
|
{{chat-scroll-bottom this.conversation.id this.messages.length}}
|
||||||
|
>
|
||||||
{{#unless this.messages.length}}
|
{{#unless this.messages.length}}
|
||||||
<p class="au-u-muted">{{@assistant.description}}</p>
|
<p class="au-u-muted">{{@assistant.description}}</p>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|||||||
16
app/helpers/format-datetime.js
Normal file
16
app/helpers/format-datetime.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { helper } from '@ember/component/helper';
|
||||||
|
|
||||||
|
const DATE = new Intl.DateTimeFormat('nl-BE', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
});
|
||||||
|
const TIME = new Intl.DateTimeFormat('nl-BE', {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default helper(([date]) => {
|
||||||
|
if (!date) return '';
|
||||||
|
return `${DATE.format(date)} ${TIME.format(date)}`;
|
||||||
|
});
|
||||||
17
app/modifiers/chat-scroll-bottom.js
Normal file
17
app/modifiers/chat-scroll-bottom.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Modifier from 'ember-modifier';
|
||||||
|
|
||||||
|
// Keeps the chat scrolled to the bottom: on insert, when another
|
||||||
|
// conversation is opened, and when the number of messages changes. The same
|
||||||
|
// conversation and count (e.g. a poll reload) leaves the scroll position
|
||||||
|
// alone, so reading history is not interrupted.
|
||||||
|
export default class ChatScrollBottom extends Modifier {
|
||||||
|
lastKey = null;
|
||||||
|
|
||||||
|
modify(element, positional) {
|
||||||
|
const [id, count] = positional;
|
||||||
|
const key = `${id}:${count}`;
|
||||||
|
if (key === this.lastKey) return;
|
||||||
|
this.lastKey = key;
|
||||||
|
element.scrollTop = element.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,13 +10,17 @@ export default class ConversationRoute extends Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async model({ id }) {
|
async model({ id }) {
|
||||||
const [conversation, agents] = await Promise.all([
|
const [conversation, agents, conversations] = await Promise.all([
|
||||||
this.store.findRecord('chat-conversation', id, {
|
this.store.findRecord('chat-conversation', id, {
|
||||||
include: 'messages.attachments',
|
include: 'messages.attachments',
|
||||||
reload: true,
|
reload: true,
|
||||||
}),
|
}),
|
||||||
this.store.query('chat-agent', { 'page[size]': 1 }),
|
this.store.query('chat-agent', { 'page[size]': 1 }),
|
||||||
|
this.store.query('chat-conversation', {
|
||||||
|
sort: '-last-activity',
|
||||||
|
'page[size]': 50,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
return { conversation, assistant: agents[0] };
|
return { conversation, assistant: agents[0], conversations };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,97 @@
|
|||||||
@import "@appuniversum/ember-appuniversum/styles/a-utilities";
|
@import "@appuniversum/ember-appuniversum/styles/a-utilities";
|
||||||
|
|
||||||
// CHAT
|
// CHAT
|
||||||
|
|
||||||
|
/* ==================================
|
||||||
|
#CHAT LAYOUT
|
||||||
|
Sidebar with the conversation overview (1/5 of the screen) next to a
|
||||||
|
content area that fills the rest of the screen under the main header.
|
||||||
|
================================== */
|
||||||
|
.chat-sidebar {
|
||||||
|
@include mq(medium) {
|
||||||
|
flex-basis: 20%;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* The conversations are listed as navigation: title on top, date + time in
|
||||||
|
subscript. Titles are truncated when they do not fit. */
|
||||||
|
.chat-sidebar__list {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.chat-sidebar__link {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.1rem;
|
||||||
|
}
|
||||||
|
.chat-sidebar__title {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.chat-sidebar__meta {
|
||||||
|
font-size: var(--au-tiny);
|
||||||
|
color: var(--au-gray-600);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.chat-sidebar__empty {
|
||||||
|
margin: 0;
|
||||||
|
padding: $au-unit-tiny * 1.5 $au-unit-small;
|
||||||
|
|
||||||
|
@include mq(medium) {
|
||||||
|
padding: $au-unit-small $au-unit-small + $au-unit-tiny;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bar above the chat: the conversation title, truncated, and a button to
|
||||||
|
start a new conversation. */
|
||||||
|
.chat-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--au-gray-200);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.chat-toolbar__title {
|
||||||
|
margin: 0;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The conversations overview: a big button to start a conversation. */
|
||||||
|
.chat-start {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: calc(100vh - 8rem);
|
||||||
|
|
||||||
|
@include mq(medium) {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chat-window {
|
.chat-window {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
height: calc(100vh - 8rem);
|
height: calc(100vh - 8rem);
|
||||||
|
|
||||||
|
@include mq(medium) {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.chat-window__messages {
|
.chat-window__messages {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -1,6 +1,21 @@
|
|||||||
<AuBodyContainer>
|
<div class="au-c-main-container__sidebar chat-sidebar">
|
||||||
|
<Chat::Sidebar @conversations={{@model.conversations}} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="content"
|
||||||
|
class="au-c-main-container__content chat-content"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div class="chat-toolbar">
|
||||||
|
<AuHeading @level="1" @skin="3" class="chat-toolbar__title">
|
||||||
|
{{if @model.conversation.title @model.conversation.title "Nieuw gesprek"}}
|
||||||
|
</AuHeading>
|
||||||
|
<Chat::NewConversation />
|
||||||
|
</div>
|
||||||
|
|
||||||
<Chat::Window
|
<Chat::Window
|
||||||
@conversation={{@model.conversation}}
|
@conversation={{@model.conversation}}
|
||||||
@assistant={{@model.assistant}}
|
@assistant={{@model.assistant}}
|
||||||
/>
|
/>
|
||||||
</AuBodyContainer>
|
</div>
|
||||||
@@ -1,22 +1,13 @@
|
|||||||
<AuBodyContainer @scroll={{true}}>
|
<div class="au-c-main-container__sidebar chat-sidebar">
|
||||||
<div class="au-o-region au-o-layout au-o-layout--small">
|
<Chat::Sidebar @conversations={{@model}} />
|
||||||
<AuHeading @level="1" @skin="2">Gesprekken</AuHeading>
|
</div>
|
||||||
<AuButton
|
|
||||||
@loading={{this.create.isRunning}}
|
<div
|
||||||
{{on "click" (perform this.create)}}
|
id="content"
|
||||||
>
|
class="au-c-main-container__content chat-content"
|
||||||
Nieuw gesprek
|
tabindex="-1"
|
||||||
</AuButton>
|
>
|
||||||
<AuList class="au-u-margin-top" as |Item|>
|
<div class="chat-start">
|
||||||
{{#each @model as |conversation|}}
|
<Chat::NewConversation @large={{true}} />
|
||||||
<Item>
|
|
||||||
<AuLink @route="conversation" @model={{conversation.id}}>
|
|
||||||
{{if conversation.title conversation.title "Nieuw gesprek"}}
|
|
||||||
</AuLink>
|
|
||||||
</Item>
|
|
||||||
{{else}}
|
|
||||||
<p class="au-u-muted">Nog geen gesprekken.</p>
|
|
||||||
{{/each}}
|
|
||||||
</AuList>
|
|
||||||
</div>
|
</div>
|
||||||
</AuBodyContainer>
|
</div>
|
||||||
Reference in New Issue
Block a user