clean up frontend

This commit is contained in:
felix
2026-09-26 18:22:27 +02:00
parent 4c81c21261
commit ac05a5ed2b
10 changed files with 199 additions and 28 deletions

View 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>

View File

@@ -1,8 +1,8 @@
import Controller from '@ember/controller';
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { task } from 'ember-concurrency';
export default class ConversationsController extends Controller {
export default class ChatNewConversation extends Component {
@service store;
@service router;
@service currentSession;

View 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>

View File

@@ -1,6 +1,9 @@
{{! The list, the composer, the poll loop }}
<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}}
<p class="au-u-muted">{{@assistant.description}}</p>
{{/unless}}

View 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)}`;
});

View 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;
}
}

View File

@@ -10,13 +10,17 @@ export default class ConversationRoute extends Route {
}
async model({ id }) {
const [conversation, agents] = await Promise.all([
const [conversation, agents, conversations] = await Promise.all([
this.store.findRecord('chat-conversation', id, {
include: 'messages.attachments',
reload: true,
}),
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 };
}
}

View File

@@ -27,10 +27,97 @@
@import "@appuniversum/ember-appuniversum/styles/a-utilities";
// 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 {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
height: calc(100vh - 8rem);
@include mq(medium) {
height: auto;
}
}
.chat-window__messages {
flex: 1;

View File

@@ -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
@conversation={{@model.conversation}}
@assistant={{@model.assistant}}
/>
</AuBodyContainer>
</div>

View File

@@ -1,22 +1,13 @@
<AuBodyContainer @scroll={{true}}>
<div class="au-o-region au-o-layout au-o-layout--small">
<AuHeading @level="1" @skin="2">Gesprekken</AuHeading>
<AuButton
@loading={{this.create.isRunning}}
{{on "click" (perform this.create)}}
>
Nieuw gesprek
</AuButton>
<AuList class="au-u-margin-top" as |Item|>
{{#each @model as |conversation|}}
<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 class="au-c-main-container__sidebar chat-sidebar">
<Chat::Sidebar @conversations={{@model}} />
</div>
<div
id="content"
class="au-c-main-container__content chat-content"
tabindex="-1"
>
<div class="chat-start">
<Chat::NewConversation @large={{true}} />
</div>
</AuBodyContainer>
</div>