Skip to content

Commit

Permalink
added note-history page
Browse files Browse the repository at this point in the history
- added note history page
- added NoteHistory domain and application services
- added noteHistory repository
  • Loading branch information
e11sy committed Jul 25, 2024
1 parent e5a7499 commit a851aa2
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 2 deletions.
7 changes: 6 additions & 1 deletion src/application/i18n/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
},
"lastEdit": "Last edit"
},
"history": {
"title": "Versions history",
"view": "View"
},
"noteList": {
"emptyNoteList": "No Notes yet. Make your first note",
"noteListItem": {
Expand Down Expand Up @@ -170,6 +174,7 @@
"addTool": "Add tool",
"notFound": "Not found",
"joinTeam": "Join",
"authorization": "Authorize"
"authorization": "Authorize",
"history": "History"
}
}
14 changes: 14 additions & 0 deletions src/application/router/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AuthorizationPage from '@/presentation/pages/AuthorizationPage.vue';
import type { RouteRecordRaw } from 'vue-router';
import AddTool from '@/presentation/pages/marketplace/AddTool.vue';
import MarketplacePage from '@/presentation/pages/marketplace/MarketplacePage.vue';
import History from '@/presentation/pages/History.vue';
import MarketplaceTools from '@/presentation/pages/marketplace/MarketplaceTools.vue';

// Default production hostname for homepage. If different, then custom hostname used
Expand Down Expand Up @@ -43,6 +44,19 @@ const routes: RouteRecordRaw[] = [
id: String(route.params.id),
}),
},
{
name: 'history',
path: '/note/:noteId/history',
component: History,
meta: {
layout: 'fullpage',
pageTitleI18n: 'pages.history',
authRequired: true,
},
props: route => ({
noteId: String(route.params.noteId),
}),
},
{
name: 'new',
path: '/new',
Expand Down
33 changes: 33 additions & 0 deletions src/application/services/useNoteHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { MaybeRefOrGetter, Ref } from 'vue';
import { computed, onMounted, ref, toValue } from 'vue';
import type { NoteHistoryRecord, NoteHistoryMeta } from '@/domain/entities/History';
import type { Note } from '@/domain/entities/Note';
import { noteHistoryService } from '@/domain';

interface UseNoteHistoryComposableState {
noteHistory: Ref<NoteHistoryMeta[] | null>;
}

interface UseNoteHistoryComposableOptions {
noteId: MaybeRefOrGetter<NoteHistoryRecord['noteId'] | null>;
}

export default function useNoteHistory(options: UseNoteHistoryComposableOptions): UseNoteHistoryComposableState {
const noteHistory = ref<NoteHistoryMeta[] | null>(null);

const currentNoteId = computed(() => toValue(options.noteId));

async function loadNoteHistory(noteId: Note['id']): Promise<void> {
noteHistory.value = await noteHistoryService.loadNoteHistory(noteId);
}

onMounted(() => {
if (currentNoteId.value !== null) {
void loadNoteHistory(currentNoteId.value);
}
});

return {
noteHistory: noteHistory,
};
}
59 changes: 59 additions & 0 deletions src/domain/entities/History.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type EditorTool from './EditorTool';
import type { Note } from './Note';
import type { User } from './User';

interface UserMeta {
/**
* Name of the user
*/
name: User['name'];

/**
* Photo of the user
*/
photo: User['photo'];
};

export interface NoteHistoryRecord {
/**
* Unique identified of note history record
*/
id: number;

/**
* Id of the note whose content history is stored
*/
noteId: Note['id'];

/**
* User that updated note content
*/
userId: User['id'];

/**
* Timestamp of note update
*/
createdAt: string;

/**
* Version of note content
*/
content: Note['content'];

/**
* Note tools of current version of note content
*/
tools: EditorTool[];
}

/**
* Meta data of the note history record
* Used for presentation of the note history record in web
*/
export type NoteHistoryMeta = Omit<NoteHistoryRecord, 'content' | 'noteId' | 'tools'> & {
/**
* Meta data of the user who did changes
* Used for note history metadata presentation
*/
user: UserMeta;
};
5 changes: 4 additions & 1 deletion src/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import NoteListService from './noteList.service';
import EditorToolsService from '@/domain/editorTools.service';
import WorkspaceService from './workspace.service';
import TeamService from './team.service';
import NoteHistoryService from './noteHistory.service';
/**
* Get API url from environment
*/
Expand Down Expand Up @@ -36,6 +37,7 @@ const authService = new AuthService(eventBus, repositories.auth);
const userService = new UserService(eventBus, repositories.user);
const marketplaceService = new MarketplaceService(repositories.marketplace);
const teamService = new TeamService(repositories.team);
const noteHistoryService = new NoteHistoryService(repositories.noteHistory);

/**
* App State — is a read-only combination of app Stores.
Expand All @@ -60,5 +62,6 @@ export {
userService,
marketplaceService,
workspaceService,
teamService
teamService,
noteHistoryService
};
10 changes: 10 additions & 0 deletions src/domain/noteHistory.repository.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { NoteHistoryMeta } from './entities/History';
import type { Note } from './entities/Note';

export default interface NoteHistoryRepositoryInterface {
/**
* Loads note history meta for note history preview
* @param noteId - id of the note
*/
loadNoteHistory(noteId: Note['id']): Promise<NoteHistoryMeta[]>;
}
15 changes: 15 additions & 0 deletions src/domain/noteHistory.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { NoteHistoryMeta } from './entities/History';
import type { Note } from './entities/Note';
import type NoteHistoryRepository from './noteHistory.repository.interface';

export default class NoteHistoryService {
private readonly noteHistoryRepository: NoteHistoryRepository;

constructor(historyRepository: NoteHistoryRepository) {
this.noteHistoryRepository = historyRepository;
}

public async loadNoteHistory(noteId: Note['id']): Promise<NoteHistoryMeta[]> {
return await this.noteHistoryRepository.loadNoteHistory(noteId);
}
}
8 changes: 8 additions & 0 deletions src/infrastructure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import EditorToolsRepository from '@/infrastructure/editorTools.repository';
import EditorToolsTransport from '@/infrastructure/transport/editorTools.transport';
import NoteAttachmentUploaderRepository from './noteAttachmentUploader.repository';
import TeamRepository from '@/infrastructure/team.repository';
import NoteHistoryRepository from './noteHistory.repository';

/**
* Repositories
Expand Down Expand Up @@ -66,6 +67,11 @@ export interface Repositories {
* Working with teams
*/
team: TeamRepository;

/**
* Working with note history
*/
noteHistory: NoteHistoryRepository;
}

/**
Expand Down Expand Up @@ -135,6 +141,7 @@ export function init(noteApiUrl: string, eventBus: EventBus): Repositories {
const noteAttachmentUploaderRepository = new NoteAttachmentUploaderRepository(notesApiTransport);
const workspaceRepository = new WorkspaceRepository(openedPagesStore);
const teamRepository = new TeamRepository(notesApiTransport);
const noteHistoryRepository = new NoteHistoryRepository(notesApiTransport);

return {
note: noteRepository,
Expand All @@ -146,5 +153,6 @@ export function init(noteApiUrl: string, eventBus: EventBus): Repositories {
noteAttachmentUploader: noteAttachmentUploaderRepository,
workspace: workspaceRepository,
team: teamRepository,
noteHistory: noteHistoryRepository,
};
}
21 changes: 21 additions & 0 deletions src/infrastructure/noteHistory.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type NoteHistoryRepositoryInterface from '@/domain/noteHistory.repository.interface';
import type NotesApiTransport from './transport/notes-api';
import type { Note } from '@/domain/entities/Note';
import type { NoteHistoryMeta } from '@/domain/entities/History';

export default class NoteHistoryRepository implements NoteHistoryRepositoryInterface {
private readonly transport: NotesApiTransport;

constructor(notesApiTransport: NotesApiTransport) {
this.transport = notesApiTransport;
}

public async loadNoteHistory(noteId: Note['id']): Promise<NoteHistoryMeta[]> {
console.log('noteIID', noteId);
const response = await this.transport.get<{ noteHistoryMeta: NoteHistoryMeta[] }>(`/note/${noteId}/history`);

console.log('resssssss', response);

return response.noteHistoryMeta;
}
}
19 changes: 19 additions & 0 deletions src/infrastructure/utils/parseDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function parseDate(date: Date): string {
// Массив с названиями месяцев
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

// Получаем название месяца
const month = months[date.getUTCMonth()];

// Получаем день
const day = date.getUTCDate();

// Получаем часы и минуты, добавляем ведущий ноль при необходимости
const hours = date.getUTCHours().toString()
.padStart(2, '0');
const minutes = date.getUTCMinutes().toString()
.padStart(2, '0');

// Формируем итоговую строку
return `${month} ${day}, ${hours}:${minutes}`;
}
120 changes: 120 additions & 0 deletions src/presentation/pages/History.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<template>
<ThreeColsLayout data-dimensions="large">
<div :class="$style['history']">
<div :class="$style['history__page-header']">
<Heading
:level="1"
:class="$style['title']"
>
{{ noteTitle }}
</Heading>
<Heading
:level="2"
:class="$style['subtle']"
>
{{ t('history.title') }}
</Heading>
</div>

<div :class="$style['history__container']">
<Container
:class="$style['history-items']"
>
<Row
v-for="(historyRecord, index) in noteHistory"
:key="historyRecord.id"
:title="historyRecord.user.name"
:subtitle="parseDate(new Date(historyRecord.createdAt))"
:has-delimiter="noteHistory !== null && index !== noteHistory?.length - 1"
:class="$style['history-items__row']"
>
<template #left>
<Avatar
:src="historyRecord.user.photo"
:username="historyRecord.user.name"
/>
</template>
<template #right>
<Button
secondary
icon="Search"
>
{{ t('history.view') }}
</Button>
</template>
</Row>
</Container>
</div>
</div>
</ThreeColsLayout>
</template>

<script setup lang="ts">
import { Heading, Container, Row, Avatar, Button } from 'codex-ui/vue';
import ThreeColsLayout from '../layouts/ThreeColsLayout.vue';
import useNoteHistory from '@/application/services/useNoteHistory';
import { parseDate } from '@/infrastructure/utils/parseDate';
const props = defineProps({
noteId: Note['id'],
});
const { noteHistory } = useNoteHistory({ noteId: props.noteId });
import { useI18n } from 'vue-i18n';
import Note from './Note.vue';
const { t } = useI18n();
const noteTitle = 'HELLO WORLD HISTORY';
</script>
<style lang="postcss" module>
.history {
display: flex;
flex-direction: column;
align-items: flex-start;
&__page-header {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: var(--spacing-s);
align-self: stretch;
padding: var(--spacing-xxl) var(--h-padding) 0;
}
&__container {
display: flex;
padding: var(--spacing-xxl) 0;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: var(--spacing-ml);
align-self: stretch;
}
}
.history-items {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
&__row {
align-self: stretch;
color: var(--text);
}
}
.title {
color: var(--base--text);
}
.subtle {
color: var(--base--text-secondary);
}
</style>

0 comments on commit a851aa2

Please sign in to comment.