From a9ef3a1d0840de2b96eba73e958f2d3c9a814c02 Mon Sep 17 00:00:00 2001 From: "andreas.kausler" Date: Fri, 19 Nov 2021 16:36:40 +0100 Subject: [PATCH 01/44] feat: add first draft of iris-message list (mockup only) --- iris-client-fe/src/App.vue | 33 ++-- iris-client-fe/src/api/api.ts | 93 ++++++++++- .../src/assets/logo-iris-connect.png | Bin 55390 -> 4052646 bytes .../src/components/data-tree/data-tree.vue | 72 ++++++++ .../src/components/pageable/search-field.vue | 54 ++++++ iris-client-fe/src/router/index.ts | 18 +- .../src/server/data/dummy-iris-messages.ts | 93 +++++++++++ iris-client-fe/src/server/mockAPIServer.ts | 23 ++- iris-client-fe/src/server/utils/pagination.ts | 40 ++++- iris-client-fe/src/store/store.config.ts | 4 +- iris-client-fe/src/store/types.ts | 2 + iris-client-fe/src/utils/pagination.ts | 2 +- .../components/iris-message-data-table.vue | 83 ++++++++++ .../components/iris-message-list-nav-link.vue | 45 +++++ .../iris-message-list.store.ts | 137 ++++++++++++++++ .../iris-message-list.view.vue | 154 ++++++++++++++++++ 16 files changed, 834 insertions(+), 19 deletions(-) create mode 100644 iris-client-fe/src/components/data-tree/data-tree.vue create mode 100644 iris-client-fe/src/components/pageable/search-field.vue create mode 100644 iris-client-fe/src/server/data/dummy-iris-messages.ts create mode 100644 iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue create mode 100644 iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue create mode 100644 iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts create mode 100644 iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue diff --git a/iris-client-fe/src/App.vue b/iris-client-fe/src/App.vue index 9233e9e53..b5a8fa622 100644 --- a/iris-client-fe/src/App.vue +++ b/iris-client-fe/src/App.vue @@ -3,26 +3,33 @@ + + diff --git a/iris-client-fe/src/components/pageable/search-field.vue b/iris-client-fe/src/components/pageable/search-field.vue new file mode 100644 index 000000000..22ec63998 --- /dev/null +++ b/iris-client-fe/src/components/pageable/search-field.vue @@ -0,0 +1,54 @@ + + + diff --git a/iris-client-fe/src/router/index.ts b/iris-client-fe/src/router/index.ts index f262efd28..d80b61fac 100644 --- a/iris-client-fe/src/router/index.ts +++ b/iris-client-fe/src/router/index.ts @@ -167,6 +167,22 @@ export const routes: Array = [ /* webpackChunkName: "index-tracking-details" */ "../views/index-tracking-details/index-tracking-details.view.vue" ), }, + { + path: "/iris-message-list", + name: "message-list" /* Caution: This acts as an identifier! */, + meta: { + menu: true, + menuName: "Nachrichten", + menuComponent: () => + import( + /* webpackChunkName: "iris-message-list-nav-link" */ "../views/iris-message-list/components/iris-message-list-nav-link.vue" + ), + }, + component: () => + import( + /* webpackChunkName: "iris-message-list" */ "../views/iris-message-list/iris-message-list.view.vue" + ), + }, { path: "/about", name: "about" /* Caution: This acts as an identifier! */, @@ -185,7 +201,7 @@ const router = new VueRouter({ routes, }); -const locationFromRoute = (route: Route): Location => { +export const locationFromRoute = (route: Route): Location => { const { name, path, hash, query, params } = route; return { ...(name ? { name } : {}), diff --git a/iris-client-fe/src/server/data/dummy-iris-messages.ts b/iris-client-fe/src/server/data/dummy-iris-messages.ts new file mode 100644 index 000000000..f001d6552 --- /dev/null +++ b/iris-client-fe/src/server/data/dummy-iris-messages.ts @@ -0,0 +1,93 @@ +import { daysAgo } from "@/server/utils/date"; +import { IrisMessage, IrisMessageContext, IrisMessageFolder } from "@/api"; + +export const dummyIrisMessageFolders: IrisMessageFolder[] = [ + { + id: "inbox", + name: "Posteingang", + context: IrisMessageContext.Inbox, + items: [ + { + id: "inbox_1", + name: "Ordner 1", + context: IrisMessageContext.Inbox, + }, + { + id: "inbox_2", + name: "Ordner 2", + context: IrisMessageContext.Inbox, + items: [ + { + id: "inbox_2_1", + name: "Ordner 2 1", + context: IrisMessageContext.Inbox, + }, + { + id: "inbox_2_2", + name: "Ordner 2 2", + context: IrisMessageContext.Inbox, + }, + ], + }, + ], + }, + { + id: "outbox", + name: "Postausgang", + context: IrisMessageContext.Outbox, + }, +]; + +export const dummyIrisMessageList: Array = [ + { + author: "Amt 1", + recipient: "Amt 3", + folder: "inbox", + id: "m1", + subject: "Indexfall-Anfrage consetetur sadipscing elitr", + body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", + createdAt: daysAgo(3), + isRead: false, + }, + { + author: "Gesundheitsamt 1", + recipient: "Gesundheitsamt 3", + folder: "outbox", + id: "2", + subject: "Austausch", + body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", + createdAt: daysAgo(1), + isRead: true, + }, + { + author: "Gesundheitsamt 2", + recipient: "Amt 3", + folder: "inbox", + id: "5", + subject: "Anfrage", + body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", + createdAt: daysAgo(5), + isRead: false, + }, + + { + author: "Gesundheit 1", + recipient: "Amt 1", + folder: "inbox", + id: "asdf", + subject: "Lorem ipsum gubergren, no sea takimata ", + body: "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", + createdAt: daysAgo(2), + isRead: true, + }, + { + author: "Muster", + recipient: "Amt 5", + folder: "outbox", + id: "271", + subject: "Test", + body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et", + createdAt: daysAgo(4), + isRead: true, + }, +]; diff --git a/iris-client-fe/src/server/mockAPIServer.ts b/iris-client-fe/src/server/mockAPIServer.ts index a017cd577..fadae1a67 100644 --- a/iris-client-fe/src/server/mockAPIServer.ts +++ b/iris-client-fe/src/server/mockAPIServer.ts @@ -5,6 +5,7 @@ import { DataRequestDetails, DataRequestStatus, ExistingDataRequestClientWithLocation, + IrisMessageQuery, User, UserRole, } from "@/api"; @@ -25,13 +26,17 @@ import { getDummyUserFromRequest, } from "@/server/data/dummy-userlist"; import { findIndex, remove, some } from "lodash"; -import { paginated } from "@/server/utils/pagination"; +import { paginated, queriedPage } from "@/server/utils/pagination"; import dayjs from "@/utils/date"; import _defaults from "lodash/defaults"; import { dummyCheckinApps, getDummyCheckinAppStatus, } from "@/server/data/status-checkin-apps"; +import { + dummyIrisMessageFolders, + dummyIrisMessageList, +} from "@/server/data/dummy-iris-messages"; const loginResponse = (role: UserRole): Response => { return new Response(200, { @@ -291,6 +296,22 @@ export function makeMockAPIServer() { getDummyCheckinAppStatus(request.params.name) ); }); + + this.get("/iris-messages", (schema, request) => { + const query: Partial = request.queryParams; + return authResponse(request, queriedPage(dummyIrisMessageList, query)); + }); + + this.get("/iris-messages/folders", (schema, request) => { + return authResponse(request, dummyIrisMessageFolders); + }); + + this.get("/iris-messages/unread/count", (schema, request) => { + return authResponse( + request, + dummyIrisMessageList.filter((item) => !item.isRead).length + ); + }); }, }); diff --git a/iris-client-fe/src/server/utils/pagination.ts b/iris-client-fe/src/server/utils/pagination.ts index df04beedc..585ffe9cc 100644 --- a/iris-client-fe/src/server/utils/pagination.ts +++ b/iris-client-fe/src/server/utils/pagination.ts @@ -1,4 +1,9 @@ -import { PageEvent, PageIndexCase } from "@/api"; +import { Page, PageEvent, PageIndexCase } from "@/api"; +import { DataQuery } from "@/api/common"; +import { DEFAULT_PAGE_SIZE } from "@/utils/pagination"; +import _orderBy from "lodash/orderBy"; +import _get from "lodash/get"; +import { TableSortDirection } from "@/views/iris-message-list/components/iris-message-data-table.vue"; type Named = { name?: string; @@ -19,3 +24,36 @@ export function paginated( }), }; } + +export const queriedPage = >( + items: T[], + query: Q +): Page => { + // @todo: add search functionality if possible + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { page, size, sort, search, ...filters } = query; + const qPage = Number(page || 1) - 1; + const qSize = Number(size || DEFAULT_PAGE_SIZE); + const qSort = (sort || "").split(","); + const sortedItems = + qSort.length > 1 + ? _orderBy(items, [qSort[0]], [qSort[1] as TableSortDirection]) + : items; + const filteredItems = sortedItems.filter((item) => { + return Object.keys(filters).find((fKey) => { + const iValue = _get(item, fKey); + const fValue = _get(filters, fKey); + if (fValue && iValue) { + return _get(item, fKey) === _get(filters, fKey); + } + return true; + }); + }); + return { + totalElements: items.length, + totalPages: Math.ceil(items.length / qSize), + size: qSize, + number: qPage, + content: filteredItems.slice(qPage * qSize, qPage * qSize + qSize), + }; +}; diff --git a/iris-client-fe/src/store/store.config.ts b/iris-client-fe/src/store/store.config.ts index e1c52e789..1f673ef32 100644 --- a/iris-client-fe/src/store/store.config.ts +++ b/iris-client-fe/src/store/store.config.ts @@ -13,13 +13,14 @@ import indexTrackingDetails from "@/views/index-tracking-details/index-tracking- import indexTrackingSettings from "@/views/app-settings/index-tracking-settings.store"; import normalizeSettings from "@/views/app-settings/normalize-settings.store"; import chunkLoader from "@/views/app-settings/chunk-loader.store"; +import checkinAppStatusList from "@/views/checkin-app-status-list/checkin-app-status-list.store"; +import irisMessageList from "@/views/iris-message-list/iris-message-list.store"; import { StoreOptions } from "vuex"; import { RootState } from "@/store/types"; import home from "@/views/home/home.store"; import createPersistedState from "vuex-persistedstate"; -import checkinAppStatusList from "@/views/checkin-app-status-list/checkin-app-status-list.store"; export const storeOptions: StoreOptions = { state: {} as RootState, @@ -42,6 +43,7 @@ export const storeOptions: StoreOptions = { normalizeSettings, chunkLoader, checkinAppStatusList, + irisMessageList, }, plugins: [ createPersistedState({ diff --git a/iris-client-fe/src/store/types.ts b/iris-client-fe/src/store/types.ts index f97f0aae6..3c2cb8e2f 100644 --- a/iris-client-fe/src/store/types.ts +++ b/iris-client-fe/src/store/types.ts @@ -14,6 +14,7 @@ import { IndexTrackingSettingsState } from "@/views/app-settings/index-tracking- import { NormalizeSettingsState } from "@/views/app-settings/normalize-settings.store"; import { ChunkLoaderState } from "@/views/app-settings/chunk-loader.store"; import { CheckinAppStatusListState } from "@/views/checkin-app-status-list/checkin-app-status-list.store"; +import { IrisMessageListState } from "@/views/iris-message-list/iris-message-list.store"; export type RootState = { home: HomeState; @@ -32,4 +33,5 @@ export type RootState = { normalizeSettings: NormalizeSettingsState; chunkLoader: ChunkLoaderState; checkinAppStatusList: CheckinAppStatusListState; + irisMessageList: IrisMessageListState; }; diff --git a/iris-client-fe/src/utils/pagination.ts b/iris-client-fe/src/utils/pagination.ts index 885b9cf2a..9d0dd4a61 100644 --- a/iris-client-fe/src/utils/pagination.ts +++ b/iris-client-fe/src/utils/pagination.ts @@ -1,7 +1,7 @@ import { Route } from "vue-router"; import { DataRequestStatus } from "@/api"; -const DEFAULT_PAGE_SIZE = 20; +export const DEFAULT_PAGE_SIZE = 20; export function getStringParamFromRouteWithOptionalFallback( param: "page" | "sort" | "search" | "status" | "size", diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue new file mode 100644 index 000000000..1c2381546 --- /dev/null +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue @@ -0,0 +1,83 @@ + + + diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue new file mode 100644 index 000000000..2df32f501 --- /dev/null +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue @@ -0,0 +1,45 @@ + + + diff --git a/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts b/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts new file mode 100644 index 000000000..da238680f --- /dev/null +++ b/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts @@ -0,0 +1,137 @@ +import { RootState } from "@/store/types"; + +import { Commit, Module } from "vuex"; +import { IrisMessageFolder, IrisMessageQuery, PageIrisMessages } from "@/api"; +import authClient from "@/api-client"; +import { ErrorMessage, getErrorMessage } from "@/utils/axios"; + +export type IrisMessageListState = { + messageList: PageIrisMessages | null; + messageListLoading: boolean; + messageListError: ErrorMessage; + messageFolders: IrisMessageFolder[] | null; + messageFoldersLoading: boolean; + messageFoldersError: ErrorMessage; + unreadMessageCount: number; +}; + +export interface IrisMessageListModule + extends Module { + mutations: { + setMessageList( + state: IrisMessageListState, + payload: PageIrisMessages + ): void; + setMessageListLoading(state: IrisMessageListState, payload: boolean): void; + setMessageListError( + state: IrisMessageListState, + payload: ErrorMessage + ): void; + setMessageFolders( + state: IrisMessageListState, + payload: IrisMessageFolder[] + ): void; + setMessageFoldersLoading( + state: IrisMessageListState, + payload: boolean + ): void; + setMessageFoldersError( + state: IrisMessageListState, + payload: ErrorMessage + ): void; + setUnreadMessageCount(state: IrisMessageListState, payload: number): void; + reset(state: IrisMessageListState, payload: null): void; + }; + actions: { + fetchMessages( + { commit }: { commit: Commit }, + payload: IrisMessageQuery + ): Promise; + fetchMessageFolders({ commit }: { commit: Commit }): Promise; + fetchUnreadMessageCount({ commit }: { commit: Commit }): Promise; + }; +} + +const defaultState: IrisMessageListState = { + messageList: { + content: [], + totalElements: 0, + }, + messageListLoading: false, + messageListError: null, + messageFolders: null, + messageFoldersLoading: false, + messageFoldersError: null, + unreadMessageCount: 0, +}; + +const irisMessageList: IrisMessageListModule = { + namespaced: true, + state() { + return { ...defaultState }; + }, + mutations: { + setMessageList(state, payload) { + state.messageList = payload; + }, + setMessageListLoading(state, payload) { + state.messageListLoading = payload; + }, + setMessageListError(state, payload) { + state.messageListError = payload; + }, + setMessageFolders(state, payload) { + state.messageFolders = payload; + }, + setMessageFoldersLoading(state, payload) { + state.messageFoldersLoading = payload; + }, + setMessageFoldersError(state, payload) { + state.messageFoldersError = payload; + }, + setUnreadMessageCount(state, payload) { + state.unreadMessageCount = payload; + }, + reset(state) { + Object.assign(state, { ...defaultState }); + }, + }, + actions: { + async fetchMessages({ commit }, query: IrisMessageQuery) { + let list: PageIrisMessages | null = null; + commit("setMessageListLoading", true); + commit("setMessageListError", null); + try { + list = (await authClient.irisMessagesGet({ params: query })).data; + } catch (e) { + commit("setMessageListError", getErrorMessage(e)); + } finally { + commit("setMessageList", list); + commit("setMessageListLoading", false); + } + }, + async fetchMessageFolders({ commit }) { + let list: IrisMessageFolder[] | null = null; + commit("setMessageFoldersLoading", true); + commit("setMessageFoldersError", null); + try { + list = (await authClient.irisMessageFoldersGet()).data; + } catch (e) { + commit("setMessageFoldersError", getErrorMessage(e)); + } finally { + commit("setMessageFolders", list); + commit("setMessageFoldersLoading", false); + } + }, + async fetchUnreadMessageCount({ commit }) { + let count = 0; + try { + count = (await authClient.irisUnreadMessageCountGet()).data; + } finally { + commit("setUnreadMessageCount", count); + } + }, + }, +}; + +export default irisMessageList; diff --git a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue new file mode 100644 index 000000000..f908d77ea --- /dev/null +++ b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue @@ -0,0 +1,154 @@ + + + From 87f6c4774159e0740c97a5c96b2a68d60158804f Mon Sep 17 00:00:00 2001 From: "andreas.kausler" Date: Mon, 22 Nov 2021 12:53:42 +0100 Subject: [PATCH 02/44] feat: improve first draft of iris-message list (mockup only) --- iris-client-fe/src/api/api.ts | 3 +- .../src/server/data/dummy-iris-messages.ts | 5 +- iris-client-fe/src/server/utils/pagination.ts | 14 +- iris-client-fe/src/utils/pagination.ts | 2 +- .../components/iris-message-data-table.vue | 10 +- .../iris-message-folders-data-tree.vue | 82 ++++++++++ .../components/iris-message-list.vue | 139 +++++++++++++++++ .../iris-message-list.view.vue | 145 ++++++------------ 8 files changed, 288 insertions(+), 112 deletions(-) create mode 100644 iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue create mode 100644 iris-client-fe/src/views/iris-message-list/components/iris-message-list.vue diff --git a/iris-client-fe/src/api/api.ts b/iris-client-fe/src/api/api.ts index cc926838f..dac86dab5 100644 --- a/iris-client-fe/src/api/api.ts +++ b/iris-client-fe/src/api/api.ts @@ -1909,7 +1909,7 @@ export interface Page { } export type IrisMessageQuery = DataQuery & { - folder: string; + folder?: string; }; export type PageIrisMessages = Page; @@ -1936,6 +1936,7 @@ export type IrisMessageFolder = { name: string; items?: IrisMessageFolder[]; context?: IrisMessageContext; + default?: boolean; }; /** diff --git a/iris-client-fe/src/server/data/dummy-iris-messages.ts b/iris-client-fe/src/server/data/dummy-iris-messages.ts index f001d6552..3bc924e4b 100644 --- a/iris-client-fe/src/server/data/dummy-iris-messages.ts +++ b/iris-client-fe/src/server/data/dummy-iris-messages.ts @@ -6,6 +6,7 @@ export const dummyIrisMessageFolders: IrisMessageFolder[] = [ id: "inbox", name: "Posteingang", context: IrisMessageContext.Inbox, + default: true, items: [ { id: "inbox_1", @@ -62,7 +63,7 @@ export const dummyIrisMessageList: Array = [ { author: "Gesundheitsamt 2", recipient: "Amt 3", - folder: "inbox", + folder: "inbox_2_1", id: "5", subject: "Anfrage", body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", @@ -73,7 +74,7 @@ export const dummyIrisMessageList: Array = [ { author: "Gesundheit 1", recipient: "Amt 1", - folder: "inbox", + folder: "outbox", id: "asdf", subject: "Lorem ipsum gubergren, no sea takimata ", body: "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", diff --git a/iris-client-fe/src/server/utils/pagination.ts b/iris-client-fe/src/server/utils/pagination.ts index 585ffe9cc..70c5e4e73 100644 --- a/iris-client-fe/src/server/utils/pagination.ts +++ b/iris-client-fe/src/server/utils/pagination.ts @@ -3,7 +3,15 @@ import { DataQuery } from "@/api/common"; import { DEFAULT_PAGE_SIZE } from "@/utils/pagination"; import _orderBy from "lodash/orderBy"; import _get from "lodash/get"; -import { TableSortDirection } from "@/views/iris-message-list/components/iris-message-data-table.vue"; + +export enum TableSortDirection { + ASC = "asc", + DESC = "desc", +} +export type TableSort = { + col: string; + dir: TableSortDirection; +}; type Named = { name?: string; @@ -50,8 +58,8 @@ export const queriedPage = >( }); }); return { - totalElements: items.length, - totalPages: Math.ceil(items.length / qSize), + totalElements: filteredItems.length, + totalPages: Math.ceil(filteredItems.length / qSize), size: qSize, number: qPage, content: filteredItems.slice(qPage * qSize, qPage * qSize + qSize), diff --git a/iris-client-fe/src/utils/pagination.ts b/iris-client-fe/src/utils/pagination.ts index 9d0dd4a61..dcda79868 100644 --- a/iris-client-fe/src/utils/pagination.ts +++ b/iris-client-fe/src/utils/pagination.ts @@ -4,7 +4,7 @@ import { DataRequestStatus } from "@/api"; export const DEFAULT_PAGE_SIZE = 20; export function getStringParamFromRouteWithOptionalFallback( - param: "page" | "sort" | "search" | "status" | "size", + param: "page" | "sort" | "search" | "status" | "size" | "folder", route: Route, fallback?: string ): string | undefined { diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue index 1c2381546..288dc936e 100644 --- a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue @@ -15,15 +15,7 @@ import { Component, Vue } from "vue-property-decorator"; import { PropType } from "vue"; import IrisDataTable from "@/components/iris-data-table.vue"; - -export enum TableSortDirection { - ASC = "asc", - DESC = "desc", -} -export type TableSort = { - col: string; - dir: TableSortDirection; -}; +import { TableSort, TableSortDirection } from "@/server/utils/pagination"; export const getSortDir = (dir: unknown): TableSortDirection | undefined => { switch (dir) { diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue new file mode 100644 index 000000000..f80ec0cd3 --- /dev/null +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue @@ -0,0 +1,82 @@ + + + diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-list.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-list.vue new file mode 100644 index 000000000..88c25fd7c --- /dev/null +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-list.vue @@ -0,0 +1,139 @@ + + + diff --git a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue index f908d77ea..7ecb3db75 100644 --- a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue +++ b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue @@ -1,23 +1,24 @@ From f939445e641b4c23ba7171e6b3edb1400ffbba6b Mon Sep 17 00:00:00 2001 From: "andreas.kausler" Date: Mon, 22 Nov 2021 16:19:22 +0100 Subject: [PATCH 03/44] feat: add first draft for message details view (mockup only) --- iris-client-fe/src/api/api.ts | 26 ++++ .../src/components/pageable/search-field.vue | 1 + .../src/components/sortable-data-table.vue | 96 ++++++++++++ iris-client-fe/src/router/index.ts | 15 +- .../src/server/data/dummy-iris-messages.ts | 20 ++- iris-client-fe/src/server/mockAPIServer.ts | 7 + iris-client-fe/src/store/store.config.ts | 2 + iris-client-fe/src/store/types.ts | 2 + .../iris-message-details.store.ts | 78 ++++++++++ .../iris-message-details.view.vue | 101 +++++++++++++ .../components/iris-message-data-table.vue | 125 +++++++++------- .../iris-message-folders-data-tree.vue | 4 +- .../components/iris-message-list.vue | 139 ------------------ .../iris-message-list.store.ts | 28 ++-- .../iris-message-list.view.vue | 69 ++++++--- 15 files changed, 479 insertions(+), 234 deletions(-) create mode 100644 iris-client-fe/src/components/sortable-data-table.vue create mode 100644 iris-client-fe/src/views/iris-message-details/iris-message-details.store.ts create mode 100644 iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue delete mode 100644 iris-client-fe/src/views/iris-message-list/components/iris-message-list.vue diff --git a/iris-client-fe/src/api/api.ts b/iris-client-fe/src/api/api.ts index dac86dab5..a5f1c8fd3 100644 --- a/iris-client-fe/src/api/api.ts +++ b/iris-client-fe/src/api/api.ts @@ -1919,6 +1919,12 @@ export enum IrisMessageContext { Outbox = "outbox", } +export interface IrisMessageAttachment { + name?: string; + type?: string; + link?: string; +} + export interface IrisMessage { name?: string; folder: string; @@ -1931,6 +1937,10 @@ export interface IrisMessage { isRead?: boolean; } +export interface IrisMessageDetails extends IrisMessage { + attachments?: IrisMessageAttachment[]; +} + export type IrisMessageFolder = { id: string; name: string; @@ -2250,6 +2260,22 @@ export class IrisClientFrontendApi extends BaseAPI { return this.apiRequest("GET", "/iris-messages", null, options); } + /** + * @summary Fetches iris message details + * @param {string} messageId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof IrisClientFrontendApi + */ + public irisMessageDetailsGet( + messageId: string, + options?: RequestOptions + ): ApiResponse { + assertParamExists("irisMessageDetailsGet", "messageId", messageId); + const path = `/iris-messages/${encodeURIComponent(messageId)}`; + return this.apiRequest("GET", path, null, options); + } + /** * @summary Fetches iris message folders * @param {*} [options] Override http request option. diff --git a/iris-client-fe/src/components/pageable/search-field.vue b/iris-client-fe/src/components/pageable/search-field.vue index 22ec63998..e5bc75616 100644 --- a/iris-client-fe/src/components/pageable/search-field.vue +++ b/iris-client-fe/src/components/pageable/search-field.vue @@ -5,6 +5,7 @@ :label="`Suchbegriff (min. ${minLength} Buchstaben)`" single-line hide-details + v-bind="$attrs" > diff --git a/iris-client-fe/src/components/sortable-data-table.vue b/iris-client-fe/src/components/sortable-data-table.vue new file mode 100644 index 000000000..e85c78733 --- /dev/null +++ b/iris-client-fe/src/components/sortable-data-table.vue @@ -0,0 +1,96 @@ + + + diff --git a/iris-client-fe/src/router/index.ts b/iris-client-fe/src/router/index.ts index d80b61fac..ccb49694e 100644 --- a/iris-client-fe/src/router/index.ts +++ b/iris-client-fe/src/router/index.ts @@ -168,8 +168,8 @@ export const routes: Array = [ ), }, { - path: "/iris-message-list", - name: "message-list" /* Caution: This acts as an identifier! */, + path: "/iris-messages/list", + name: "iris-message-list" /* Caution: This acts as an identifier! */, meta: { menu: true, menuName: "Nachrichten", @@ -183,6 +183,17 @@ export const routes: Array = [ /* webpackChunkName: "iris-message-list" */ "../views/iris-message-list/iris-message-list.view.vue" ), }, + { + path: "/iris-messages/details/:messageId", + name: "iris-message-details" /* Caution: This acts as an identifier! */, + meta: { + menu: false, + }, + component: () => + import( + /* webpackChunkName: "iris-message-details" */ "../views/iris-message-details/iris-message-details.view.vue" + ), + }, { path: "/about", name: "about" /* Caution: This acts as an identifier! */, diff --git a/iris-client-fe/src/server/data/dummy-iris-messages.ts b/iris-client-fe/src/server/data/dummy-iris-messages.ts index 3bc924e4b..81a578b59 100644 --- a/iris-client-fe/src/server/data/dummy-iris-messages.ts +++ b/iris-client-fe/src/server/data/dummy-iris-messages.ts @@ -1,5 +1,9 @@ import { daysAgo } from "@/server/utils/date"; -import { IrisMessage, IrisMessageContext, IrisMessageFolder } from "@/api"; +import { + IrisMessageContext, + IrisMessageDetails, + IrisMessageFolder, +} from "@/api"; export const dummyIrisMessageFolders: IrisMessageFolder[] = [ { @@ -39,7 +43,7 @@ export const dummyIrisMessageFolders: IrisMessageFolder[] = [ }, ]; -export const dummyIrisMessageList: Array = [ +export const dummyIrisMessageList: Array = [ { author: "Amt 1", recipient: "Amt 3", @@ -49,6 +53,18 @@ export const dummyIrisMessageList: Array = [ body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", createdAt: daysAgo(3), isRead: false, + attachments: [ + { + name: "anhang 1", + type: "pdf", + link: "testlink", + }, + { + name: "Liste 2", + type: "csv", + link: "testlink_2", + }, + ], }, { author: "Gesundheitsamt 1", diff --git a/iris-client-fe/src/server/mockAPIServer.ts b/iris-client-fe/src/server/mockAPIServer.ts index fadae1a67..6e160862e 100644 --- a/iris-client-fe/src/server/mockAPIServer.ts +++ b/iris-client-fe/src/server/mockAPIServer.ts @@ -302,6 +302,13 @@ export function makeMockAPIServer() { return authResponse(request, queriedPage(dummyIrisMessageList, query)); }); + this.get("/iris-messages/:messageId", (schema, request) => { + const message = dummyIrisMessageList.find( + (msg) => msg.id === request.params.messageId + ); + return authResponse(request, message); + }); + this.get("/iris-messages/folders", (schema, request) => { return authResponse(request, dummyIrisMessageFolders); }); diff --git a/iris-client-fe/src/store/store.config.ts b/iris-client-fe/src/store/store.config.ts index 1f673ef32..61e79d1e4 100644 --- a/iris-client-fe/src/store/store.config.ts +++ b/iris-client-fe/src/store/store.config.ts @@ -15,6 +15,7 @@ import normalizeSettings from "@/views/app-settings/normalize-settings.store"; import chunkLoader from "@/views/app-settings/chunk-loader.store"; import checkinAppStatusList from "@/views/checkin-app-status-list/checkin-app-status-list.store"; import irisMessageList from "@/views/iris-message-list/iris-message-list.store"; +import irisMessageDetails from "@/views/iris-message-details/iris-message-details.store"; import { StoreOptions } from "vuex"; import { RootState } from "@/store/types"; @@ -44,6 +45,7 @@ export const storeOptions: StoreOptions = { chunkLoader, checkinAppStatusList, irisMessageList, + irisMessageDetails, }, plugins: [ createPersistedState({ diff --git a/iris-client-fe/src/store/types.ts b/iris-client-fe/src/store/types.ts index 3c2cb8e2f..f7868b5da 100644 --- a/iris-client-fe/src/store/types.ts +++ b/iris-client-fe/src/store/types.ts @@ -15,6 +15,7 @@ import { NormalizeSettingsState } from "@/views/app-settings/normalize-settings. import { ChunkLoaderState } from "@/views/app-settings/chunk-loader.store"; import { CheckinAppStatusListState } from "@/views/checkin-app-status-list/checkin-app-status-list.store"; import { IrisMessageListState } from "@/views/iris-message-list/iris-message-list.store"; +import { IrisMessageDetailsState } from "@/views/iris-message-details/iris-message-details.store"; export type RootState = { home: HomeState; @@ -34,4 +35,5 @@ export type RootState = { chunkLoader: ChunkLoaderState; checkinAppStatusList: CheckinAppStatusListState; irisMessageList: IrisMessageListState; + irisMessageDetails: IrisMessageDetailsState; }; diff --git a/iris-client-fe/src/views/iris-message-details/iris-message-details.store.ts b/iris-client-fe/src/views/iris-message-details/iris-message-details.store.ts new file mode 100644 index 000000000..cf150dfe7 --- /dev/null +++ b/iris-client-fe/src/views/iris-message-details/iris-message-details.store.ts @@ -0,0 +1,78 @@ +import { RootState } from "@/store/types"; + +import { Commit, Module } from "vuex"; +import { IrisMessageDetails } from "@/api"; +import authClient from "@/api-client"; +import { ErrorMessage, getErrorMessage } from "@/utils/axios"; + +export type IrisMessageDetailsState = { + message: IrisMessageDetails | null; + messageLoading: boolean; + messageLoadingError: ErrorMessage; +}; + +export interface IrisMessageDetailsModule + extends Module { + mutations: { + setMessage( + state: IrisMessageDetailsState, + payload: IrisMessageDetails + ): void; + setMessageLoading(state: IrisMessageDetailsState, payload: boolean): void; + setMessageLoadingError( + state: IrisMessageDetailsState, + payload: ErrorMessage + ): void; + reset(state: IrisMessageDetailsState, payload: null): void; + }; + actions: { + fetchMessage( + { commit }: { commit: Commit }, + messageId: string + ): Promise; + }; +} + +const defaultState: IrisMessageDetailsState = { + message: null, + messageLoading: false, + messageLoadingError: null, +}; + +const irisMessageDetails: IrisMessageDetailsModule = { + namespaced: true, + state() { + return { ...defaultState }; + }, + mutations: { + setMessage(state, payload) { + state.message = payload; + }, + setMessageLoading(state, payload) { + state.messageLoading = payload; + }, + setMessageLoadingError(state, payload) { + state.messageLoadingError = payload; + }, + reset(state) { + Object.assign(state, { ...defaultState }); + }, + }, + actions: { + async fetchMessage({ commit }, messageId: string) { + let data: IrisMessageDetails | null = null; + commit("setMessageLoading", true); + commit("setMessageLoadingError", null); + try { + data = (await authClient.irisMessageDetailsGet(messageId)).data; + } catch (e) { + commit("setMessageLoadingError", getErrorMessage(e)); + } finally { + commit("setMessage", data); + commit("setMessageLoading", false); + } + }, + }, +}; + +export default irisMessageDetails; diff --git a/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue b/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue new file mode 100644 index 000000000..6fa2ceb2b --- /dev/null +++ b/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue @@ -0,0 +1,101 @@ + + + diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue index 288dc936e..19e6ac173 100644 --- a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue @@ -1,75 +1,98 @@ diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue index f80ec0cd3..e5a29c411 100644 --- a/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-folders-data-tree.vue @@ -1,11 +1,11 @@ - - diff --git a/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts b/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts index da238680f..b853d6a39 100644 --- a/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts +++ b/iris-client-fe/src/views/iris-message-list/iris-message-list.store.ts @@ -8,10 +8,10 @@ import { ErrorMessage, getErrorMessage } from "@/utils/axios"; export type IrisMessageListState = { messageList: PageIrisMessages | null; messageListLoading: boolean; - messageListError: ErrorMessage; + messageListLoadingError: ErrorMessage; messageFolders: IrisMessageFolder[] | null; messageFoldersLoading: boolean; - messageFoldersError: ErrorMessage; + messageFoldersLoadingError: ErrorMessage; unreadMessageCount: number; }; @@ -23,7 +23,7 @@ export interface IrisMessageListModule payload: PageIrisMessages ): void; setMessageListLoading(state: IrisMessageListState, payload: boolean): void; - setMessageListError( + setMessageListLoadingError( state: IrisMessageListState, payload: ErrorMessage ): void; @@ -35,7 +35,7 @@ export interface IrisMessageListModule state: IrisMessageListState, payload: boolean ): void; - setMessageFoldersError( + setMessageFoldersLoadingError( state: IrisMessageListState, payload: ErrorMessage ): void; @@ -58,10 +58,10 @@ const defaultState: IrisMessageListState = { totalElements: 0, }, messageListLoading: false, - messageListError: null, + messageListLoadingError: null, messageFolders: null, messageFoldersLoading: false, - messageFoldersError: null, + messageFoldersLoadingError: null, unreadMessageCount: 0, }; @@ -77,8 +77,8 @@ const irisMessageList: IrisMessageListModule = { setMessageListLoading(state, payload) { state.messageListLoading = payload; }, - setMessageListError(state, payload) { - state.messageListError = payload; + setMessageListLoadingError(state, payload) { + state.messageListLoadingError = payload; }, setMessageFolders(state, payload) { state.messageFolders = payload; @@ -86,8 +86,8 @@ const irisMessageList: IrisMessageListModule = { setMessageFoldersLoading(state, payload) { state.messageFoldersLoading = payload; }, - setMessageFoldersError(state, payload) { - state.messageFoldersError = payload; + setMessageFoldersLoadingError(state, payload) { + state.messageFoldersLoadingError = payload; }, setUnreadMessageCount(state, payload) { state.unreadMessageCount = payload; @@ -100,11 +100,11 @@ const irisMessageList: IrisMessageListModule = { async fetchMessages({ commit }, query: IrisMessageQuery) { let list: PageIrisMessages | null = null; commit("setMessageListLoading", true); - commit("setMessageListError", null); + commit("setMessageListLoadingError", null); try { list = (await authClient.irisMessagesGet({ params: query })).data; } catch (e) { - commit("setMessageListError", getErrorMessage(e)); + commit("setMessageListLoadingError", getErrorMessage(e)); } finally { commit("setMessageList", list); commit("setMessageListLoading", false); @@ -113,11 +113,11 @@ const irisMessageList: IrisMessageListModule = { async fetchMessageFolders({ commit }) { let list: IrisMessageFolder[] | null = null; commit("setMessageFoldersLoading", true); - commit("setMessageFoldersError", null); + commit("setMessageFoldersLoadingError", null); try { list = (await authClient.irisMessageFoldersGet()).data; } catch (e) { - commit("setMessageFoldersError", getErrorMessage(e)); + commit("setMessageFoldersLoadingError", getErrorMessage(e)); } finally { commit("setMessageFolders", list); commit("setMessageFoldersLoading", false); diff --git a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue index 7ecb3db75..046186abe 100644 --- a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue +++ b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue @@ -1,23 +1,30 @@ @@ -32,15 +39,15 @@ import { import SearchField from "@/components/pageable/search-field.vue"; import { IrisMessageFolder, IrisMessageQuery } from "@/api"; import DataTree from "@/components/data-tree/data-tree.vue"; -import IrisMessageList from "@/views/iris-message-list/components/iris-message-list.vue"; import ErrorMessageAlert from "@/components/error-message-alert.vue"; import IrisMessageFoldersDataTree from "@/views/iris-message-list/components/iris-message-folders-data-tree.vue"; +import IrisMessageDataTable from "@/views/iris-message-list/components/iris-message-data-table.vue"; @Component({ components: { + IrisMessageDataTable, IrisMessageFoldersDataTree, ErrorMessageAlert, - IrisMessageList, DataTree, SearchField, }, @@ -52,8 +59,8 @@ import IrisMessageFoldersDataTree from "@/views/iris-message-list/components/iri export default class IrisMessageListView extends Vue { get errors() { return [ - this.$store.state.irisMessageList.messageListError, - this.$store.state.irisMessageList.messageFoldersError, + this.$store.state.irisMessageList.messageListLoadingError, + this.$store.state.irisMessageList.messageFoldersLoadingError, ]; } @@ -82,12 +89,26 @@ export default class IrisMessageListView extends Vue { }; } - get foldersLoading(): boolean { - return this.$store.state.irisMessageList.messageFoldersLoading; + get messageList(): IrisMessageFolder[] | null { + return this.$store.state.irisMessageList.messageList; + } + get messageListLoading(): boolean { + return this.$store.state.irisMessageList.messageListLoading; } + get folders(): IrisMessageFolder[] | null { return this.$store.state.irisMessageList.messageFolders; } + get foldersLoading(): boolean { + return this.$store.state.irisMessageList.messageFoldersLoading; + } + + handleRowClick(row: { id: string }) { + this.$router.push({ + name: "iris-message-details", + params: { messageId: row.id }, + }); + } updateRoute(query: Record): void { this.$router From 17a2079b2a5e71ccf3aac9abd4f71e08baba701c Mon Sep 17 00:00:00 2001 From: "andreas.kausler" Date: Tue, 23 Nov 2021 14:34:16 +0100 Subject: [PATCH 04/44] feat: add first draft for message creation view (mockup only) --- iris-client-fe/src/api-client.ts | 6 +- iris-client-fe/src/api/api.ts | 80 +++++++- iris-client-fe/src/api/common.ts | 26 ++- .../src/assets/scss/base/_global.scss | 4 + iris-client-fe/src/router/index.ts | 11 ++ .../src/server/data/dummy-iris-messages.ts | 79 ++++++-- iris-client-fe/src/server/mockAPIServer.ts | 17 ++ iris-client-fe/src/store/store.config.ts | 2 + iris-client-fe/src/store/types.ts | 2 + .../iris-message-create.store.ts | 109 +++++++++++ .../iris-message-create.view.vue | 171 ++++++++++++++++++ .../iris-message-details.view.vue | 35 +++- .../components/iris-message-data-table.vue | 6 +- .../components/iris-message-list-nav-link.vue | 7 + .../iris-message-list.view.vue | 5 + 15 files changed, 526 insertions(+), 34 deletions(-) create mode 100644 iris-client-fe/src/views/iris-message-create/iris-message-create.store.ts create mode 100644 iris-client-fe/src/views/iris-message-create/iris-message-create.view.vue diff --git a/iris-client-fe/src/api-client.ts b/iris-client-fe/src/api-client.ts index d6f44f049..b89093cab 100644 --- a/iris-client-fe/src/api-client.ts +++ b/iris-client-fe/src/api-client.ts @@ -19,7 +19,11 @@ const { apiBaseURL } = config; const clientConfig: AxiosRequestConfig = { baseURL: apiBaseURL, - transformRequest: (data) => { + transformRequest: (data, headers) => { + if (data instanceof FormData) { + if (headers) headers["Content-Type"] = "multipart/form-data"; + return data; + } return JSON.stringify(data !== undefined ? data : {}); }, }; diff --git a/iris-client-fe/src/api/api.ts b/iris-client-fe/src/api/api.ts index a5f1c8fd3..eeecf3c20 100644 --- a/iris-client-fe/src/api/api.ts +++ b/iris-client-fe/src/api/api.ts @@ -1914,11 +1914,6 @@ export type IrisMessageQuery = DataQuery & { export type PageIrisMessages = Page; -export enum IrisMessageContext { - Inbox = "inbox", - Outbox = "outbox", -} - export interface IrisMessageAttachment { name?: string; type?: string; @@ -1931,9 +1926,9 @@ export interface IrisMessage { id: string; subject: string; body: string; - author?: string; - recipient?: string; - createdAt?: string; + author: IrisMessageContact; + recipient: IrisMessageContact; + createdAt: string; isRead?: boolean; } @@ -1941,6 +1936,18 @@ export interface IrisMessageDetails extends IrisMessage { attachments?: IrisMessageAttachment[]; } +export interface IrisMessageInsert { + recipient: string; + subject: string; + body: string; + attachments?: File[]; +} + +export enum IrisMessageContext { + Inbox = "inbox", + Outbox = "outbox", +} + export type IrisMessageFolder = { id: string; name: string; @@ -1949,6 +1956,11 @@ export type IrisMessageFolder = { default?: boolean; }; +export interface IrisMessageContact { + id: string; + name: string; +} + /** * IrisClientFrontendApi - object-oriented interface * @export @@ -2276,6 +2288,25 @@ export class IrisClientFrontendApi extends BaseAPI { return this.apiRequest("GET", path, null, options); } + /** + * + * @summary Create IRIS message + * @param {IrisMessageInsert} data + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof IrisClientFrontendApi + */ + public irisMessagesPost( + data: IrisMessageInsert, + options?: RequestOptions + ): ApiResponse { + assertParamExists("irisMessagesPost", "data", data); + return this.apiRequest("POST", "/iris-messages", data, { + ...options, + formData: true, + }); + } + /** * @summary Fetches iris message folders * @param {*} [options] Override http request option. @@ -2288,6 +2319,18 @@ export class IrisClientFrontendApi extends BaseAPI { return this.apiRequest("GET", "/iris-messages/folders", null, options); } + /** + * @summary Fetches iris message contacts + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof IrisClientFrontendApi + */ + public irisMessageContactsGet( + options?: RequestOptions + ): ApiResponse { + return this.apiRequest("GET", "/iris-messages/contacts", null, options); + } + /** * @summary Fetches number of unread messages * @param {*} [options] Override http request option. @@ -2299,4 +2342,25 @@ export class IrisClientFrontendApi extends BaseAPI { ): ApiResponse { return this.apiRequest("GET", "/iris-messages/unread/count", null, options); } + + /** + * + * @summary Mark IRIS message as read + * @param {string} messageId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof IrisClientFrontendApi + */ + public irisMessagesMarkRead( + messageId: string, + options?: RequestOptions + ): ApiResponse { + assertParamExists("irisMessagesMarkRead", "messageId", messageId); + return this.apiRequest( + "POST", + "/iris-messages/:messageId/mark-read", + messageId, + options + ); + } } diff --git a/iris-client-fe/src/api/common.ts b/iris-client-fe/src/api/common.ts index 88c9dea6d..ff237429c 100644 --- a/iris-client-fe/src/api/common.ts +++ b/iris-client-fe/src/api/common.ts @@ -7,7 +7,6 @@ import globalAxios, { AxiosResponse, Method, } from "axios"; - /** * * @export @@ -27,6 +26,7 @@ export type RequestQuery = { export interface RequestOptions extends AxiosRequestConfig { query?: RequestQuery; + formData?: boolean; } export type ApiResponse = Promise>; @@ -121,10 +121,32 @@ export const apiRequestBuilder = ): ApiResponse => { const url = new URL(path, "https://example.com"); url.search = createSearchParams(url, options?.query || {}); + const requestData = + options?.formData && data + ? createFormData(data as Record) + : data; return axiosInstance.request({ url: toPathString(url), - data, + data: requestData, method, ...options, }); }; + +const createFormData = ( + data: Record +): FormData => { + const formData = new FormData(); + let key: keyof typeof data; + for (key in data) { + const entry = data[key]; + if (Array.isArray(entry)) { + entry.forEach((item) => { + formData.append(`${key}`, item); + }); + } else { + formData.append(key, entry); + } + } + return formData; +}; diff --git a/iris-client-fe/src/assets/scss/base/_global.scss b/iris-client-fe/src/assets/scss/base/_global.scss index 87022c6e5..9e77551ac 100644 --- a/iris-client-fe/src/assets/scss/base/_global.scss +++ b/iris-client-fe/src/assets/scss/base/_global.scss @@ -40,3 +40,7 @@ a:hover { left: 20%; right: 20%; } + +.cursor-pointer { + cursor: pointer; +} \ No newline at end of file diff --git a/iris-client-fe/src/router/index.ts b/iris-client-fe/src/router/index.ts index ccb49694e..6e35e9373 100644 --- a/iris-client-fe/src/router/index.ts +++ b/iris-client-fe/src/router/index.ts @@ -194,6 +194,17 @@ export const routes: Array = [ /* webpackChunkName: "iris-message-details" */ "../views/iris-message-details/iris-message-details.view.vue" ), }, + { + path: "/iris-messages/create", + name: "iris-message-create" /* Caution: This acts as an identifier! */, + meta: { + menu: false, + }, + component: () => + import( + /* webpackChunkName: "iris-message-create" */ "../views/iris-message-create/iris-message-create.view.vue" + ), + }, { path: "/about", name: "about" /* Caution: This acts as an identifier! */, diff --git a/iris-client-fe/src/server/data/dummy-iris-messages.ts b/iris-client-fe/src/server/data/dummy-iris-messages.ts index 81a578b59..4b020a2d9 100644 --- a/iris-client-fe/src/server/data/dummy-iris-messages.ts +++ b/iris-client-fe/src/server/data/dummy-iris-messages.ts @@ -3,7 +3,9 @@ import { IrisMessageContext, IrisMessageDetails, IrisMessageFolder, + IrisMessageContact, } from "@/api"; +import { Request } from "miragejs"; export const dummyIrisMessageFolders: IrisMessageFolder[] = [ { @@ -43,10 +45,33 @@ export const dummyIrisMessageFolders: IrisMessageFolder[] = [ }, ]; -export const dummyIrisMessageList: Array = [ +export const dummyIrisMessageContacts: IrisMessageContact[] = [ { - author: "Amt 1", - recipient: "Amt 3", + id: "1", + name: "Eigenes GA", + }, + { + id: "2", + name: "Kontakt 2", + }, + { + id: "3", + name: "Kontakt 3", + }, + { + id: "4", + name: "Kontakt 4", + }, + { + id: "5", + name: "Kontakt 5", + }, +]; + +export const dummyIrisMessageList: IrisMessageDetails[] = [ + { + author: dummyIrisMessageContacts[1], + recipient: dummyIrisMessageContacts[0], folder: "inbox", id: "m1", subject: "Indexfall-Anfrage consetetur sadipscing elitr", @@ -67,8 +92,8 @@ export const dummyIrisMessageList: Array = [ ], }, { - author: "Gesundheitsamt 1", - recipient: "Gesundheitsamt 3", + author: dummyIrisMessageContacts[0], + recipient: dummyIrisMessageContacts[4], folder: "outbox", id: "2", subject: "Austausch", @@ -77,8 +102,8 @@ export const dummyIrisMessageList: Array = [ isRead: true, }, { - author: "Gesundheitsamt 2", - recipient: "Amt 3", + author: dummyIrisMessageContacts[2], + recipient: dummyIrisMessageContacts[0], folder: "inbox_2_1", id: "5", subject: "Anfrage", @@ -88,8 +113,8 @@ export const dummyIrisMessageList: Array = [ }, { - author: "Gesundheit 1", - recipient: "Amt 1", + author: dummyIrisMessageContacts[0], + recipient: dummyIrisMessageContacts[2], folder: "outbox", id: "asdf", subject: "Lorem ipsum gubergren, no sea takimata ", @@ -98,9 +123,9 @@ export const dummyIrisMessageList: Array = [ isRead: true, }, { - author: "Muster", - recipient: "Amt 5", - folder: "outbox", + author: dummyIrisMessageContacts[3], + recipient: dummyIrisMessageContacts[0], + folder: "inbox", id: "271", subject: "Test", body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et", @@ -108,3 +133,33 @@ export const dummyIrisMessageList: Array = [ isRead: true, }, ]; + +export const getDummyMessageFromRequest = ( + request: Request, + id?: string +): IrisMessageDetails => { + const form = request.requestBody as unknown as FormData; + const subject = form.get("subject") as string; + const body = form.get("body") as string; + const recipient = form.get("recipient") as string; + const attachments = form.getAll("attachments"); + return { + id: id || new Date().getTime() + "", + subject, + body, + folder: "outbox", + author: dummyIrisMessageContacts[0], + recipient: + dummyIrisMessageContacts.find((c) => c.id === recipient) || + dummyIrisMessageContacts[1], + createdAt: new Date().getTime() + "", + attachments: attachments.map((attachment) => { + const a = attachment as File; + return { + name: a.name, + type: a.type, + link: a.name, + }; + }), + }; +}; diff --git a/iris-client-fe/src/server/mockAPIServer.ts b/iris-client-fe/src/server/mockAPIServer.ts index 6e160862e..a656352df 100644 --- a/iris-client-fe/src/server/mockAPIServer.ts +++ b/iris-client-fe/src/server/mockAPIServer.ts @@ -36,6 +36,8 @@ import { import { dummyIrisMessageFolders, dummyIrisMessageList, + dummyIrisMessageContacts, + getDummyMessageFromRequest, } from "@/server/data/dummy-iris-messages"; const loginResponse = (role: UserRole): Response => { @@ -309,10 +311,25 @@ export function makeMockAPIServer() { return authResponse(request, message); }); + this.post("/iris-messages", (schema, request) => { + try { + if (validateAuthHeader(request)) { + dummyIrisMessageList.push(getDummyMessageFromRequest(request)); + } + } catch (e) { + // ignored + } + return authResponse(request); + }); + this.get("/iris-messages/folders", (schema, request) => { return authResponse(request, dummyIrisMessageFolders); }); + this.get("/iris-messages/contacts", (schema, request) => { + return authResponse(request, dummyIrisMessageContacts); + }); + this.get("/iris-messages/unread/count", (schema, request) => { return authResponse( request, diff --git a/iris-client-fe/src/store/store.config.ts b/iris-client-fe/src/store/store.config.ts index 61e79d1e4..d39371f37 100644 --- a/iris-client-fe/src/store/store.config.ts +++ b/iris-client-fe/src/store/store.config.ts @@ -16,6 +16,7 @@ import chunkLoader from "@/views/app-settings/chunk-loader.store"; import checkinAppStatusList from "@/views/checkin-app-status-list/checkin-app-status-list.store"; import irisMessageList from "@/views/iris-message-list/iris-message-list.store"; import irisMessageDetails from "@/views/iris-message-details/iris-message-details.store"; +import irisMessageCreate from "@/views/iris-message-create/iris-message-create.store"; import { StoreOptions } from "vuex"; import { RootState } from "@/store/types"; @@ -46,6 +47,7 @@ export const storeOptions: StoreOptions = { checkinAppStatusList, irisMessageList, irisMessageDetails, + irisMessageCreate, }, plugins: [ createPersistedState({ diff --git a/iris-client-fe/src/store/types.ts b/iris-client-fe/src/store/types.ts index f7868b5da..3130b4cdf 100644 --- a/iris-client-fe/src/store/types.ts +++ b/iris-client-fe/src/store/types.ts @@ -16,6 +16,7 @@ import { ChunkLoaderState } from "@/views/app-settings/chunk-loader.store"; import { CheckinAppStatusListState } from "@/views/checkin-app-status-list/checkin-app-status-list.store"; import { IrisMessageListState } from "@/views/iris-message-list/iris-message-list.store"; import { IrisMessageDetailsState } from "@/views/iris-message-details/iris-message-details.store"; +import { IrisMessageCreateState } from "@/views/iris-message-create/iris-message-create.store"; export type RootState = { home: HomeState; @@ -36,4 +37,5 @@ export type RootState = { checkinAppStatusList: CheckinAppStatusListState; irisMessageList: IrisMessageListState; irisMessageDetails: IrisMessageDetailsState; + irisMessageCreate: IrisMessageCreateState; }; diff --git a/iris-client-fe/src/views/iris-message-create/iris-message-create.store.ts b/iris-client-fe/src/views/iris-message-create/iris-message-create.store.ts new file mode 100644 index 000000000..61971aa79 --- /dev/null +++ b/iris-client-fe/src/views/iris-message-create/iris-message-create.store.ts @@ -0,0 +1,109 @@ +import { RootState } from "@/store/types"; + +import { Commit, Module } from "vuex"; +import authClient from "@/api-client"; +import { ErrorMessage, getErrorMessage } from "@/utils/axios"; +import { IrisMessageInsert, IrisMessageContact } from "@/api"; + +export type IrisMessageCreateState = { + messageCreationOngoing: boolean; + messageCreationError: ErrorMessage; + contacts: IrisMessageContact[] | null; + contactsLoading: boolean; + contactsLoadingError: ErrorMessage; +}; + +export interface IrisMessageDetailsModule + extends Module { + mutations: { + setMessageCreationOngoing( + state: IrisMessageCreateState, + payload: boolean + ): void; + setMessageCreationError( + state: IrisMessageCreateState, + payload: ErrorMessage + ): void; + setContacts( + state: IrisMessageCreateState, + payload: IrisMessageContact[] + ): void; + setContactsLoading(state: IrisMessageCreateState, payload: boolean): void; + setContactsLoadingError( + state: IrisMessageCreateState, + payload: ErrorMessage + ): void; + reset(state: IrisMessageCreateState, payload: null): void; + }; + actions: { + createMessage( + { commit }: { commit: Commit }, + data: IrisMessageInsert + ): Promise; + fetchRecipients({ commit }: { commit: Commit }): Promise; + }; +} + +const defaultState: IrisMessageCreateState = { + messageCreationOngoing: false, + messageCreationError: null, + contacts: null, + contactsLoading: false, + contactsLoadingError: null, +}; + +const irisMessageCreate: IrisMessageDetailsModule = { + namespaced: true, + state() { + return { ...defaultState }; + }, + mutations: { + setMessageCreationOngoing(state, payload) { + state.messageCreationOngoing = payload; + }, + setMessageCreationError(state, payload) { + state.messageCreationError = payload; + }, + setContacts(state, payload) { + state.contacts = payload; + }, + setContactsLoading(state, payload) { + state.contactsLoading = payload; + }, + setContactsLoadingError(state, payload) { + state.contactsLoadingError = payload; + }, + reset(state) { + Object.assign(state, { ...defaultState }); + }, + }, + actions: { + async createMessage({ commit }, data) { + commit("setMessageCreationError", null); + commit("setMessageCreationOngoing", true); + try { + await authClient.irisMessagesPost(data); + } catch (e) { + commit("setMessageCreationError", getErrorMessage(e)); + throw e; + } finally { + commit("setMessageCreationOngoing", false); + } + }, + async fetchRecipients({ commit }) { + let list: IrisMessageContact[] | null = null; + commit("setContactsLoading", true); + commit("setContactsLoadingError", null); + try { + list = (await authClient.irisMessageContactsGet()).data; + } catch (e) { + commit("setContactsLoadingError", getErrorMessage(e)); + } finally { + commit("setContacts", list); + commit("setContactsLoading", false); + } + }, + }, +}; + +export default irisMessageCreate; diff --git a/iris-client-fe/src/views/iris-message-create/iris-message-create.view.vue b/iris-client-fe/src/views/iris-message-create/iris-message-create.view.vue new file mode 100644 index 000000000..7ca4e2d72 --- /dev/null +++ b/iris-client-fe/src/views/iris-message-create/iris-message-create.view.vue @@ -0,0 +1,171 @@ + + + diff --git a/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue b/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue index 6fa2ceb2b..f8e197539 100644 --- a/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue +++ b/iris-client-fe/src/views/iris-message-details/iris-message-details.view.vue @@ -2,7 +2,7 @@
- {{ getFormattedDate(message.createdAt) }} + {{ message.createdAt }} {{ message.subject }} @@ -11,18 +11,18 @@ Von: - {{ message.author || "-" }} + {{ message.author }} An: - {{ message.recipient || "-" }} + {{ message.recipient }}
{{ message.body }}
-
+

Anhang

@@ -65,10 +65,19 @@ import { Component, Vue } from "vue-property-decorator"; import store from "@/store"; import ErrorMessageAlert from "@/components/error-message-alert.vue"; -import { IrisMessageDetails } from "@/api"; +import { IrisMessageAttachment, IrisMessageDetails } from "@/api"; import { ErrorMessage } from "@/utils/axios"; import { getFormattedDate } from "@/utils/date"; +type MessageData = { + author: string; + recipient: string; + createdAt: string; + subject: string; + body: string; + attachments: IrisMessageAttachment[]; +}; + @Component({ components: { ErrorMessageAlert, @@ -83,9 +92,19 @@ import { getFormattedDate } from "@/utils/date"; }, }) export default class IrisMessageDetailsView extends Vue { - getFormattedDate = getFormattedDate; - get message(): IrisMessageDetails { - return this.$store.state.irisMessageDetails.message || {}; + get message(): MessageData { + const message: IrisMessageDetails | null = + this.$store.state.irisMessageDetails.message; + return { + author: message?.author?.name || "-", + recipient: message?.recipient?.name || "-", + createdAt: message?.createdAt + ? getFormattedDate(message?.createdAt) + : "-", + subject: message?.subject || "-", + body: message?.body || "-", + attachments: message?.attachments || [], + }; } get messageLoading(): boolean { return this.$store.state.irisMessageDetails.messageLoading; diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue index 19e6ac173..88d6706aa 100644 --- a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue @@ -51,7 +51,7 @@ export default class IrisMessageDataTable extends IrisMessageDataTableProps { get tableHeaders(): DataTableHeader[] { if (this.context === IrisMessageContext.Inbox) { return [ - { text: "Von", value: "author", sortable: true }, + { text: "Von", value: "author.name", sortable: true }, { text: "Betreff", value: "subject", @@ -62,7 +62,7 @@ export default class IrisMessageDataTable extends IrisMessageDataTableProps { } if (this.context === IrisMessageContext.Outbox) { return [ - { text: "An", value: "recipient", sortable: true }, + { text: "An", value: "recipient.name", sortable: true }, { text: "Betreff", value: "subject", @@ -92,7 +92,7 @@ export default class IrisMessageDataTable extends IrisMessageDataTableProps { }; } itemClass(item: { isRead: boolean }) { - return item.isRead ? "" : "font-weight-bold"; + return item.isRead ? "cursor-pointer" : "cursor-pointer font-weight-bold"; } } diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue index 2df32f501..5d9e7cdca 100644 --- a/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-list-nav-link.vue @@ -12,6 +12,7 @@ color="blue" :content="unreadMessageCount" :value="unreadMessageCount > 0" + class="badge" > {{ link.meta.menuName }} @@ -43,3 +44,9 @@ export default class IrisMessageListNavLink extends IrisMessageListNavLinkProps } } + + diff --git a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue index 046186abe..ec68637b3 100644 --- a/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue +++ b/iris-client-fe/src/views/iris-message-list/iris-message-list.view.vue @@ -1,5 +1,10 @@ diff --git a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue index 52e14abab..8b6ce3201 100644 --- a/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue +++ b/iris-client-fe/src/views/iris-message-list/components/iris-message-data-table.vue @@ -4,8 +4,9 @@ v-if="context" class="mt-5" v-bind="{ ...dataTable, ...$attrs }" - v-on="$listeners" - :footer-props="{ 'items-per-page-options': [10, 20, 30, 50] }" + v-on="listeners" + :page.sync="tablePage" + :footer-props="{ 'items-per-page-options': [2, 10, 20, 30, 50] }" :item-class="itemClass" >