erudit 3.0.0-dev.18 → 3.0.0-dev.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/app/assets/icons/graduation.svg +3 -0
  2. package/app/components/aside/AsideMinor.vue +41 -17
  3. package/app/components/aside/major/panes/Pages.vue +11 -14
  4. package/app/components/aside/minor/{Contribute.vue → AsideMinorContribute.vue} +35 -5
  5. package/app/components/aside/minor/content/AsideMinorContent.vue +7 -10
  6. package/app/components/aside/minor/contributor/AsideMinorContributor.vue +78 -0
  7. package/app/components/aside/minor/contributor/BookContribution.vue +64 -0
  8. package/app/components/aside/minor/topic/AsideMinorTopic.vue +1 -4
  9. package/app/components/aside/minor/topic/TopicContributors.vue +3 -3
  10. package/app/components/aside/minor/topic/TopicNav.vue +6 -6
  11. package/app/components/aside/minor/topic/TopicToc.vue +1 -2
  12. package/app/components/bitran/BitranContent.vue +15 -16
  13. package/app/components/contributor/ContributorAvatar.vue +3 -3
  14. package/app/components/main/MainBitranContent.vue +41 -0
  15. package/app/components/main/{utils/Breadcrumb.vue → MainBreadcrumb.vue} +12 -19
  16. package/app/components/main/MainDescription.vue +24 -0
  17. package/app/components/main/{utils/ContentTitle.vue → MainTitle.vue} +11 -2
  18. package/app/components/main/content/ContentBreadcrumb.vue +28 -0
  19. package/app/components/main/{utils → content}/ContentSection.vue +2 -2
  20. package/app/components/main/topic/MainTopic.vue +15 -20
  21. package/app/components/main/topic/TopicPartSwitch.vue +9 -3
  22. package/app/components/preview/display/Unique.vue +2 -11
  23. package/app/composables/adsAllowed.ts +1 -1
  24. package/app/composables/majorPane.ts +3 -2
  25. package/app/composables/phrases.ts +21 -9
  26. package/app/pages/book/[...bookId].vue +6 -11
  27. package/app/pages/contributor/[contributorId].vue +225 -0
  28. package/app/pages/contributors.vue +183 -0
  29. package/app/pages/group/[...groupId].vue +11 -19
  30. package/app/scripts/preview/data/unique.ts +18 -21
  31. package/languages/en.ts +12 -3
  32. package/languages/ru.ts +12 -3
  33. package/package.json +5 -5
  34. package/server/api/aside/minor/book/[...bookId].ts +18 -0
  35. package/server/api/aside/minor/contributor/[contributorId].ts +18 -0
  36. package/server/api/aside/minor/group/[...groupId].ts +18 -0
  37. package/server/api/aside/minor/news.ts +2 -2
  38. package/server/api/aside/minor/topic.ts +36 -0
  39. package/server/api/bitran/content/[...location].ts +4 -1
  40. package/server/api/contributor/list.ts +44 -0
  41. package/server/api/contributor/page/[contributorId].ts +14 -0
  42. package/server/api/preview/unique/[...location].ts +10 -23
  43. package/server/plugin/bitran/content.ts +34 -19
  44. package/server/plugin/bitran/location.ts +6 -2
  45. package/server/plugin/build/jobs/contributors.ts +3 -0
  46. package/server/plugin/db/entities/Contributor.ts +9 -0
  47. package/server/plugin/repository/asideMinor.ts +51 -0
  48. package/server/plugin/repository/book.ts +16 -0
  49. package/server/plugin/repository/contributor.ts +90 -0
  50. package/shared/aside/minor.ts +32 -28
  51. package/shared/bitran/content.ts +9 -0
  52. package/shared/breadcrumb.ts +7 -0
  53. package/shared/contributor.ts +28 -0
  54. package/shared/types/language.ts +8 -3
  55. package/app/components/aside/minor/AsideMinorContributor.vue +0 -5
  56. package/app/components/main/utils/ContentDescription.vue +0 -19
  57. package/app/composables/bitranContent.ts +0 -96
  58. package/app/pages/members.vue +0 -5
  59. package/server/api/aside/minor/path.ts +0 -82
  60. package/shared/bitran/stringContent.ts +0 -6
  61. /package/app/components/main/{utils → content}/ContentDecoration.vue +0 -0
  62. /package/app/components/main/{utils → content}/ContentPopover.vue +0 -0
  63. /package/app/components/main/{utils → content}/ContentPopovers.vue +0 -0
  64. /package/app/components/main/{utils → content}/ContentReferences.vue +0 -0
  65. /package/app/components/main/{utils → content}/reference/ReferenceGroup.vue +0 -0
  66. /package/app/components/main/{utils → content}/reference/ReferenceItem.vue +0 -0
  67. /package/app/components/main/{utils → content}/reference/ReferenceSource.vue +0 -0
@@ -0,0 +1,183 @@
1
+ <script lang="ts" setup>
2
+ import eruditConfig from '#erudit/config';
3
+
4
+ const { data: contributorList } = await useFetch(`/api/contributor/list`, {
5
+ key: 'contributor-list',
6
+ });
7
+
8
+ const editorsNumber = computed(() => {
9
+ return contributorList.value?.filter((i) => i.isEditor)?.length ?? 0;
10
+ });
11
+
12
+ const phrase = await usePhrases(
13
+ 'contributors',
14
+ 'contributors_page_description',
15
+ 'contributors_page_invite',
16
+ 'contributions_explain',
17
+ 'editor',
18
+ );
19
+
20
+ useHead({
21
+ title:
22
+ phrase.contributors +
23
+ ' - ' +
24
+ (eruditConfig.seo?.title || eruditConfig.site?.title),
25
+ });
26
+
27
+ useSeoMeta({
28
+ title:
29
+ phrase.contributors +
30
+ ' - ' +
31
+ (eruditConfig.seo?.title || eruditConfig.site?.title),
32
+ description: phrase.contributors_page_description,
33
+ });
34
+
35
+ const fullDescription = (() => {
36
+ let _description = phrase.contributors_page_description;
37
+
38
+ if (eruditConfig.content?.howToImproveLink) {
39
+ _description +=
40
+ ' ' +
41
+ phrase.contributors_page_invite(
42
+ eruditConfig.content.howToImproveLink,
43
+ );
44
+ }
45
+
46
+ return _description;
47
+ })();
48
+ </script>
49
+
50
+ <template>
51
+ <MainTitle icon="users" :title="phrase.contributors" />
52
+ <MainDescription
53
+ :class="$style.pageDescription"
54
+ :html="true"
55
+ :description="fullDescription"
56
+ />
57
+ <section :class="$style.contributors">
58
+ <NuxtLink
59
+ :to="`/contributor/${contributor.contributorId}`"
60
+ :prefetch="false"
61
+ v-for="(contributor, i) of contributorList"
62
+ :class="$style.contributor"
63
+ :style="{
64
+ '--contributorColor': stringColor(contributor.contributorId),
65
+ }"
66
+ >
67
+ <ContributorAvatar
68
+ :contributorId="contributor.contributorId"
69
+ :avatar="contributor.avatar"
70
+ :class="$style.avatar"
71
+ />
72
+ <div :class="$style.info">
73
+ <div :class="$style.main">
74
+ <div :class="$style.position">
75
+ <template v-if="contributor.isEditor">
76
+ <MyIcon
77
+ :title="phrase.editor"
78
+ :class="$style.editor"
79
+ name="graduation"
80
+ />
81
+ </template>
82
+ <template v-else>
83
+ {{ i - editorsNumber + 1 }}.
84
+ </template>
85
+ </div>
86
+ <div :class="$style.name">
87
+ {{
88
+ contributor.displayName || contributor.contributorId
89
+ }}
90
+ </div>
91
+ </div>
92
+ <div :class="$style.contributions">
93
+ {{
94
+ phrase.contributions_explain(contributor.contributions)
95
+ }}
96
+ </div>
97
+ </div>
98
+ </NuxtLink>
99
+ </section>
100
+ </template>
101
+
102
+ <style lang="scss" module>
103
+ @use '$/def/bp';
104
+
105
+ .pageDescription a {
106
+ color: inherit;
107
+ text-decoration-style: dashed;
108
+ text-decoration-color: var(--textDimmed);
109
+
110
+ &:hover {
111
+ text-decoration-style: solid;
112
+ text-decoration-color: var(--text);
113
+ }
114
+ }
115
+
116
+ .contributors {
117
+ padding: var(--_pMainY) var(--_pMainX);
118
+ display: grid;
119
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
120
+ gap: var(--gap);
121
+
122
+ @include bp.below('mobile') {
123
+ padding: var(--_pMainY) 0;
124
+ gap: 0;
125
+ }
126
+
127
+ .contributor {
128
+ display: flex;
129
+ align-items: center;
130
+ gap: var(--gap);
131
+ padding: var(--gap);
132
+ border-radius: 5px;
133
+ text-decoration: none;
134
+ color: inherit;
135
+ @include transition(background);
136
+
137
+ &:hover {
138
+ background: var(--bgAccent);
139
+ }
140
+
141
+ @include bp.below('mobile') {
142
+ border-radius: 0;
143
+ }
144
+
145
+ .avatar {
146
+ --_avatarSize: 50px;
147
+ flex-shrink: 0;
148
+ box-shadow: 0 0 5px 5px
149
+ color-mix(in srgb, var(--contributorColor), transparent 85%);
150
+ }
151
+
152
+ .info {
153
+ display: flex;
154
+ flex-direction: column;
155
+
156
+ .main {
157
+ display: flex;
158
+ align-items: center;
159
+ gap: 5px;
160
+
161
+ .position {
162
+ font-size: 0.9em;
163
+ font-weight: 600;
164
+ color: var(--textMuted);
165
+
166
+ .editor {
167
+ cursor: help;
168
+ }
169
+ }
170
+
171
+ .name {
172
+ font-weight: 600;
173
+ }
174
+ }
175
+
176
+ .contributions {
177
+ color: var(--textMuted);
178
+ font-size: 0.85em;
179
+ }
180
+ }
181
+ }
182
+ }
183
+ </style>
@@ -1,22 +1,20 @@
1
1
  <script lang="ts" setup>
2
- import { NO_ALIASES } from '@erudit-js/cog/schema';
2
+ import { type BitranLocation } from '@erudit-js/cog/schema';
3
3
  import eruditConfig from '#erudit/config';
4
4
 
5
5
  import { type ContentGroupData } from '@shared/content/data/type/group';
6
6
  import { locationIcon } from '@erudit/shared/icons';
7
- import ContentDecoration from '@app/components/main/utils/ContentDecoration.vue';
8
- import Breadcrumb from '@app/components/main/utils/Breadcrumb.vue';
9
- import ContentTitle from '@app/components/main/utils/ContentTitle.vue';
10
- import ContentDescription from '@app/components/main/utils/ContentDescription.vue';
11
- import ContentPopovers from '@app/components/main/utils/ContentPopovers.vue';
12
- import ContentSection from '@app/components/main/utils/ContentSection.vue';
13
7
 
14
- const location = useBitranLocation();
8
+ import ContentBreadcrumb from '@app/components/main/content/ContentBreadcrumb.vue';
9
+ import ContentDecoration from '@app/components/main/content/ContentDecoration.vue';
10
+ import ContentPopovers from '@app/components/main/content/ContentPopovers.vue';
11
+ import ContentSection from '@app/components/main/content/ContentSection.vue';
12
+
13
+ const location = useBitranLocation() as Ref<BitranLocation>;
15
14
 
16
15
  const groupData = await useContentData<ContentGroupData>();
17
16
  await useContentPage(groupData);
18
17
 
19
- const content = await useBitranContent(location);
20
18
  const phrase = await usePhrases('group');
21
19
  </script>
22
20
 
@@ -26,12 +24,9 @@ const phrase = await usePhrases('group');
26
24
  :decoration="groupData.generic.decoration"
27
25
  />
28
26
 
29
- <Breadcrumb
30
- v-if="groupData.generic.context?.length > 1"
31
- :context="groupData.generic.context"
32
- />
27
+ <ContentBreadcrumb :context="groupData.generic.context" />
33
28
 
34
- <ContentTitle
29
+ <MainTitle
35
30
  :title="
36
31
  groupData.generic?.title ||
37
32
  groupData.generic.contentId.split('/').pop()!
@@ -40,7 +35,7 @@ const phrase = await usePhrases('group');
40
35
  :hint="phrase.group"
41
36
  />
42
37
 
43
- <ContentDescription
38
+ <MainDescription
44
39
  v-if="groupData.generic?.description"
45
40
  :description="groupData.generic?.description"
46
41
  />
@@ -54,10 +49,7 @@ const phrase = await usePhrases('group');
54
49
  <hr style="display: none" />
55
50
 
56
51
  <ContentSection>
57
- <BitranContent
58
- :content
59
- :context="{ location, aliases: NO_ALIASES() }"
60
- />
52
+ <MainBitranContent :location />
61
53
  </ContentSection>
62
54
 
63
55
  <ContentSection v-if="adsAllowed() && eruditConfig.ads?.bottom">
@@ -1,47 +1,46 @@
1
1
  import {
2
2
  encodeBitranLocation,
3
- type BitranContext,
3
+ parseBitranLocation,
4
4
  } from '@erudit-js/cog/schema';
5
5
 
6
+ import type { RawBitranContent } from '@shared/bitran/content';
7
+
6
8
  import { PreviewDataType, type PreviewDataBase } from '../data';
7
9
  import type { PreviewFooter } from '../footer';
8
10
  import { PreviewRequestType, type PreviewRequest } from '../request';
9
- import type { StringBitranContent } from '@erudit/shared/bitran/stringContent';
10
11
 
11
12
  export interface PreviewDataUnique extends PreviewDataBase {
12
13
  type: PreviewDataType.Unique;
13
- productName: string;
14
- bitran: {
15
- context: BitranContext;
16
- content: StringBitranContent;
17
- };
14
+ elementName: string;
15
+ title: string;
16
+ rawBitranContent: RawBitranContent;
18
17
  footer: PreviewFooter;
19
18
  }
20
19
 
21
20
  export async function buildUnique(
22
21
  request: PreviewRequest,
23
- ): Promise<PreviewDataUnique> {
22
+ ): Promise<PreviewDataUnique | undefined> {
24
23
  if (request.type !== PreviewRequestType.Link) return;
25
24
 
26
25
  const { linkTarget } = request;
27
26
 
28
27
  if (linkTarget.type !== 'unique') return;
29
28
 
30
- const serverData = (await $fetch(
29
+ const serverData = await $fetch(
31
30
  `/api/preview/unique/${encodeBitranLocation(linkTarget._absoluteStrLocation!)}`,
32
31
  { responseType: 'json' },
33
- )) as any;
34
- const productName = serverData.bitran.productName;
35
- const customTitle = serverData.productTitle;
32
+ );
33
+ const elementName = serverData.elementName;
34
+ const customTitle = serverData.title;
36
35
 
37
- const icon = await useBitranElementIcon(productName);
36
+ const icon = await useBitranElementIcon(elementName);
38
37
 
39
38
  const productPhraseName = await (async () => {
40
- const elementPhrase = await useBitranElementLanguage(productName);
39
+ const elementPhrase = await useBitranElementLanguage(elementName);
41
40
  try {
42
41
  return elementPhrase('_element_title');
43
42
  } catch {
44
- return productName;
43
+ return elementName;
45
44
  }
46
45
  })();
47
46
 
@@ -53,15 +52,13 @@ export async function buildUnique(
53
52
  ? (secondary ? ' • ' : '') + productPhraseName
54
53
  : '';
55
54
 
56
- const primary = customTitle || productPhraseName || productName;
55
+ const primary = customTitle || productPhraseName || elementName;
57
56
 
58
57
  return {
59
58
  type: PreviewDataType.Unique,
60
- productName,
61
- bitran: {
62
- context: serverData.bitran.context,
63
- content: serverData.bitran.content as any,
64
- },
59
+ elementName: elementName,
60
+ title: customTitle || productPhraseName,
61
+ rawBitranContent: serverData.rawBitranContent as RawBitranContent,
65
62
  footer: {
66
63
  iconSvg: icon,
67
64
  primary,
package/languages/en.ts CHANGED
@@ -26,7 +26,18 @@ const english: EruditPhrases = {
26
26
  theme_dark: 'Dark',
27
27
  content: 'Content',
28
28
  main_page: 'Main page',
29
- members: 'Members',
29
+ contributors: 'Contributors',
30
+ contributors_page_description:
31
+ 'List of people who contributed to the project materials: suggested valuable ideas, made corrections to existing material or wrote their own!',
32
+ contributors_page_invite: (link) =>
33
+ `You can also help the project, <a href="${link}" target="_blank">make a contribution</a> and get on this page!`,
34
+ contributor: 'Contributor',
35
+ contribution: 'Contribution',
36
+ contributions_explain: (count) =>
37
+ `Contributed to ${m(count, 'material', 'materials')}`,
38
+ contributor_description: (name) =>
39
+ `Page with information about the contributor "${name}" and his contribution to the project.`,
40
+ editor: 'Editor',
30
41
  add_translation: 'Add translation',
31
42
  empty_nav: 'Empty navigation',
32
43
  flag_dev: 'Development',
@@ -57,7 +68,6 @@ const english: EruditPhrases = {
57
68
  article: 'Article',
58
69
  summary: 'Summary',
59
70
  practice: 'Practice',
60
- contributor: 'Contributor',
61
71
  element_id: 'Element ID',
62
72
  preview_missing_title: 'Element not found!',
63
73
  preview_missing_explain: `Can't find the element with specified ID in this page!<br>Perhaps the element ID is specified incorrectly or the element has been changed/deleted.`,
@@ -68,7 +78,6 @@ const english: EruditPhrases = {
68
78
  current_page_hash: 'Current hash',
69
79
  expected_page_hash: 'Expected hash',
70
80
  empty_toc: 'Empty table of contents...',
71
- contributors: 'Contributors',
72
81
  no_contributors: 'No contributors...',
73
82
  make_contribution: 'Make a contribution',
74
83
  material_improvement: 'Material improvement',
package/languages/ru.ts CHANGED
@@ -26,7 +26,18 @@ const russian: EruditPhrases = {
26
26
  theme_dark: 'Темная',
27
27
  content: 'Контент',
28
28
  main_page: 'Главная страница',
29
- members: 'Участники',
29
+ contributors: 'Авторы',
30
+ contributors_page_description:
31
+ 'Список людей, которые внесли вклад в материалы проекта: предложили ценные идеи, вносили корректировки в существующий материал или же написали собственный!',
32
+ contributors_page_invite: (link) =>
33
+ `Вы тоже можете помочь проекту, <a href="${link}" target="_blank">внести свой вклад</a> и попасть на эту страницу!`,
34
+ contributor: 'Автор',
35
+ contribution: 'Вклад',
36
+ contributions_explain: (count) =>
37
+ `Вклад в ${m(count, 'материал', 'материала', 'материалов')}`,
38
+ contributor_description: (name) =>
39
+ `Страница с информацией о авторе "${name}" и его вкладе в проект.`,
40
+ editor: 'Редактор',
30
41
  add_translation: 'Добавить перевод',
31
42
  empty_nav: 'Пустая навигация',
32
43
  flag_dev: 'Разработка',
@@ -57,7 +68,6 @@ const russian: EruditPhrases = {
57
68
  article: 'Статья',
58
69
  summary: 'Конспект',
59
70
  practice: 'Задачи',
60
- contributor: 'Автор',
61
71
  element_id: 'ID элемента',
62
72
  preview_missing_title: 'Элемент не найден!',
63
73
  preview_missing_explain:
@@ -69,7 +79,6 @@ const russian: EruditPhrases = {
69
79
  current_page_hash: 'Текущий хеш',
70
80
  expected_page_hash: 'Ожидаемый хеш',
71
81
  empty_toc: 'Пустая таблица содержимого...',
72
- contributors: 'Авторы',
73
82
  no_contributors: 'Авторов нет...',
74
83
  make_contribution: 'Внести свой вклад',
75
84
  material_improvement: 'Улучшение материала',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "erudit",
3
- "version": "3.0.0-dev.18",
3
+ "version": "3.0.0-dev.19",
4
4
  "type": "module",
5
5
  "description": "🤓 CMS for perfect educational sites.",
6
6
  "license": "MIT",
@@ -15,9 +15,9 @@
15
15
  "erudit": "bin/erudit.mjs"
16
16
  },
17
17
  "peerDependencies": {
18
- "@erudit-js/cog": "3.0.0-dev.18",
19
- "@erudit-js/cli": "3.0.0-dev.18",
20
- "@erudit-js/bitran-elements": "3.0.0-dev.18"
18
+ "@erudit-js/cog": "3.0.0-dev.19",
19
+ "@erudit-js/cli": "3.0.0-dev.19",
20
+ "@erudit-js/bitran-elements": "3.0.0-dev.19"
21
21
  },
22
22
  "dependencies": {
23
23
  "@bitran-js/core": "1.0.0-dev.13",
@@ -31,7 +31,7 @@
31
31
  "glob": "^11.0.2",
32
32
  "image-size": "^2.0.2",
33
33
  "jiti": "^2.4.2",
34
- "nuxt": "3.17.2",
34
+ "nuxt": "3.17.3",
35
35
  "nuxt-my-icons": "1.1.1",
36
36
  "sass": "^1.87.0",
37
37
  "sqlite3": "^5.1.7",
@@ -0,0 +1,18 @@
1
+ import {
2
+ getAsideMinorContentBase,
3
+ getAsideMinorContentNode,
4
+ } from '@server/repository/asideMinor';
5
+
6
+ import type { AsideMinorBook } from '@erudit/shared/aside/minor';
7
+
8
+ export default defineEventHandler(async (event) => {
9
+ const bookNavNode = getAsideMinorContentNode(
10
+ 'book',
11
+ event.context.params?.bookId,
12
+ );
13
+
14
+ return {
15
+ type: 'book',
16
+ ...(await getAsideMinorContentBase(bookNavNode)),
17
+ } satisfies AsideMinorBook;
18
+ });
@@ -0,0 +1,18 @@
1
+ import { getContributions } from '@server/repository/contributor';
2
+ import type { AsideMinorContributor } from '@erudit/shared/aside/minor';
3
+
4
+ export default defineEventHandler(async (event) => {
5
+ const contributorId = event.context.params?.contributorId;
6
+
7
+ if (!contributorId) {
8
+ throw createError({
9
+ statusCode: 400,
10
+ message: 'Missing contributor ID!',
11
+ });
12
+ }
13
+
14
+ return {
15
+ type: 'contributor',
16
+ contributions: await getContributions(contributorId),
17
+ } satisfies AsideMinorContributor;
18
+ });
@@ -0,0 +1,18 @@
1
+ import {
2
+ getAsideMinorContentNode,
3
+ getAsideMinorContentBase,
4
+ } from '@server/repository/asideMinor';
5
+
6
+ import type { AsideMinorGroup } from '@erudit/shared/aside/minor';
7
+
8
+ export default defineEventHandler(async (event) => {
9
+ const groupNavNode = getAsideMinorContentNode(
10
+ 'group',
11
+ event.context.params?.groupId,
12
+ );
13
+
14
+ return {
15
+ type: 'group',
16
+ ...(await getAsideMinorContentBase(groupNavNode)),
17
+ } satisfies AsideMinorGroup;
18
+ });
@@ -1,7 +1,7 @@
1
1
  import type { AsideMinorNews } from '@shared/aside/minor';
2
2
 
3
3
  export default defineEventHandler(async () => {
4
- return <AsideMinorNews>{
4
+ return {
5
5
  type: 'news',
6
- };
6
+ } satisfies AsideMinorNews;
7
7
  });
@@ -0,0 +1,36 @@
1
+ import type { TopicPart } from '@erudit-js/cog/schema';
2
+
3
+ import { getBitranToc } from '@server/bitran/toc';
4
+ import {
5
+ getAsideMinorContentBase,
6
+ getAsideMinorContentNode,
7
+ } from '@server/repository/asideMinor';
8
+ import { getTopicPartsLinks } from '@server/repository/topic';
9
+
10
+ import type { AsideMinorTopic } from '@erudit/shared/aside/minor';
11
+
12
+ export default defineEventHandler(async (event) => {
13
+ const query = getQuery<{ topicId: string; topicPart: TopicPart }>(event);
14
+ const topicNavNode = getAsideMinorContentNode('topic', query.topicId);
15
+ const topicPart = query.topicPart;
16
+
17
+ if (!topicPart) {
18
+ throw createError({
19
+ statusCode: 400,
20
+ statusText: 'Missing "topicPart" query parameter!',
21
+ });
22
+ }
23
+
24
+ const toc = await getBitranToc({
25
+ type: topicPart,
26
+ path: topicNavNode.fullId,
27
+ });
28
+
29
+ return {
30
+ type: 'topic',
31
+ toc,
32
+ part: topicPart,
33
+ partLinks: await getTopicPartsLinks(topicNavNode.fullId),
34
+ ...(await getAsideMinorContentBase(topicNavNode)),
35
+ } satisfies AsideMinorTopic;
36
+ });
@@ -1,10 +1,13 @@
1
1
  import { parseClientBitranLocation } from '@server/bitran/location';
2
2
  import { getBitranContent } from '@server/bitran/content';
3
3
 
4
- export default defineEventHandler(async (event) => {
4
+ import { type RawBitranContent } from '@shared/bitran/content';
5
+
6
+ export default defineEventHandler<Promise<RawBitranContent>>(async (event) => {
5
7
  setResponseHeader(event, 'Content-Type', 'application/json; charset=utf-8');
6
8
  const location = await parseClientBitranLocation(
7
9
  event.context.params!.location,
8
10
  );
11
+
9
12
  return await getBitranContent(location);
10
13
  });
@@ -0,0 +1,44 @@
1
+ import { DbContribution } from '@server/db/entities/Contribution';
2
+ import { DbContributor } from '@server/db/entities/Contributor';
3
+ import { ERUDIT_SERVER } from '@server/global';
4
+
5
+ import { type ContributorListItem } from '@shared/contributor';
6
+
7
+ export default defineEventHandler<Promise<ContributorListItem[]>>(async () => {
8
+ const dbContributors = await ERUDIT_SERVER.DB.manager.find(DbContributor, {
9
+ select: ['contributorId', 'displayName', 'isEditor', 'avatar'],
10
+ });
11
+
12
+ if (dbContributors.length === 0) {
13
+ return [];
14
+ }
15
+
16
+ const contributors: ContributorListItem[] = await Promise.all(
17
+ dbContributors.map(async (dbContributor) => {
18
+ const contributions = await ERUDIT_SERVER.DB.manager.count(
19
+ DbContribution,
20
+ {
21
+ where: { contributorId: dbContributor.contributorId },
22
+ },
23
+ );
24
+
25
+ return {
26
+ contributorId: dbContributor.contributorId,
27
+ displayName: dbContributor.displayName,
28
+ isEditor: dbContributor.isEditor,
29
+ avatar: dbContributor.avatar,
30
+ contributions,
31
+ };
32
+ }),
33
+ );
34
+
35
+ contributors.sort((a, b) => {
36
+ if (a.isEditor !== b.isEditor) {
37
+ return a.isEditor ? -1 : 1;
38
+ }
39
+
40
+ return b.contributions - a.contributions;
41
+ });
42
+
43
+ return contributors;
44
+ });
@@ -0,0 +1,14 @@
1
+ import { getContributorPageData } from '@server/repository/contributor';
2
+
3
+ export default defineEventHandler(async (event) => {
4
+ const contributorId = event.context.params?.contributorId;
5
+
6
+ if (!contributorId) {
7
+ throw createError({
8
+ statusCode: 400,
9
+ message: 'Missing contributor ID!',
10
+ });
11
+ }
12
+
13
+ return await getContributorPageData(contributorId);
14
+ });
@@ -1,25 +1,19 @@
1
- import {
2
- stringifyBitranLocation,
3
- type BitranContext,
4
- } from '@erudit-js/cog/schema';
1
+ import { stringifyBitranLocation } from '@erudit-js/cog/schema';
5
2
 
6
3
  import { getLocationContext } from '@server/content/context';
7
4
  import { ERUDIT_SERVER } from '@server/global';
8
5
  import { DbUnique } from '@server/db/entities/Unique';
9
- import { getBitranContent } from '@server/bitran/content';
10
6
  import { parseClientBitranLocation } from '@server/bitran/location';
7
+ import { getBitranContent } from '@server/bitran/content';
11
8
 
12
9
  import type { Context } from '@shared/content/context';
13
- import type { StringBitranContent } from '@erudit/shared/bitran/stringContent';
10
+ import type { RawBitranContent } from '@shared/bitran/content';
14
11
 
15
12
  interface ReturnType {
16
13
  context: Context;
17
- productTitle?: string;
18
- bitran: {
19
- productName: string;
20
- context: BitranContext;
21
- content: StringBitranContent;
22
- };
14
+ title?: string;
15
+ elementName: string;
16
+ rawBitranContent: RawBitranContent;
23
17
  }
24
18
 
25
19
  export default defineEventHandler<Promise<ReturnType>>(async (event) => {
@@ -33,7 +27,7 @@ export default defineEventHandler<Promise<ReturnType>>(async (event) => {
33
27
 
34
28
  const dbUnique = await (async () => {
35
29
  const dbUnique = ERUDIT_SERVER.DB.manager.findOne(DbUnique, {
36
- select: ['productName', 'title', 'context'],
30
+ select: ['title', 'productName'],
37
31
  where: { location: stringifyBitranLocation(location) },
38
32
  });
39
33
 
@@ -45,17 +39,10 @@ export default defineEventHandler<Promise<ReturnType>>(async (event) => {
45
39
  return dbUnique as any as DbUnique;
46
40
  })();
47
41
 
48
- const bitran = await (async () => {
49
- return {
50
- content: await getBitranContent(location),
51
- context: dbUnique.context,
52
- productName: dbUnique.productName,
53
- };
54
- })();
55
-
56
42
  return <ReturnType>{
57
43
  context,
58
- productTitle: dbUnique.title,
59
- bitran,
44
+ title: dbUnique.title,
45
+ elementName: dbUnique.productName,
46
+ rawBitranContent: await getBitranContent(location),
60
47
  };
61
48
  });