@tager/admin-blog 1.0.1 → 1.0.2
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.
- package/dist/dev/App.vue.d.ts +1 -1
- package/dist/index.mjs +56 -14
- package/dist/locales/en.d.ts +5 -0
- package/dist/services/requests.d.ts +1 -0
- package/dist/style.css +1 -1
- package/dist/views/Categories/CategoryForm/CategoryForm.vue.d.ts +1 -1
- package/dist/views/Categories/CategoryList/CategoryList.vue.d.ts +1 -1
- package/dist/views/Posts/PostForm/PostForm.vue.d.ts +1 -1
- package/dist/views/Posts/PostList/PostList.helpers.d.ts +2 -1
- package/dist/views/Posts/PostList/PostList.vue.d.ts +3 -1
- package/package.json +4 -4
- package/src/hooks/useFetchPosts.ts +2 -1
- package/src/locales/en.ts +7 -1
- package/src/locales/ru.ts +7 -1
- package/src/services/requests.ts +1 -0
- package/src/views/Posts/PostForm/PostForm.vue +8 -3
- package/src/views/Posts/PostList/PostList.helpers.ts +26 -1
- package/src/views/Posts/PostList/PostList.vue +109 -98
package/dist/dev/App.vue.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MenuItemType } from '@tager/admin-layout';
|
|
2
2
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
3
3
|
sidebarMenuList: MenuItemType[];
|
|
4
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin,
|
|
4
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
5
5
|
export default _default;
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { onMounted, watch, computed, ref, defineComponent, resolveComponent, ope
|
|
|
3
3
|
import isEqual from "lodash.isequal";
|
|
4
4
|
import pick from "lodash.pick";
|
|
5
5
|
import { useRoute, useRouter } from "vue-router";
|
|
6
|
-
import { getFilterParamAsStringArray, getFilterParamAsString, getFilterParams, DeleteIcon, ContentCopyIcon, EditIcon, BaseButton, FormField, FormFieldMultiSelect, AdvancedSearch, DataTable, useDataTable, formatDateTime, SeoFieldGroup, FormFieldFileInput, FormFieldRichTextInput, FormFieldCheckbox, FormFieldUrlAliasInput, FormFieldSelect, TabList, ShortCodeConstructor, FormFooter, createTabErrorFinder, SouthIcon, NorthIcon, CountButton } from "@tager/admin-ui";
|
|
6
|
+
import { getFilterParamAsStringArray, getFilterParamAsString, getFilterParams, DeleteIcon, ContentCopyIcon, EditIcon, BaseButton, FormField, FormFieldMultiSelect, AdvancedSearch, DataTable, useDataTable, formatDateTime, SeoFieldGroup, FormFieldFileInput, FormFieldRichTextInput, FormFieldCheckbox, FormFieldUrlAliasInput, FormFieldSelect, TabList, ShortCodeConstructor, FormFooter, formatDate, createTabErrorFinder, SouthIcon, NorthIcon, CountButton } from "@tager/admin-ui";
|
|
7
7
|
import { Page } from "@tager/admin-layout";
|
|
8
8
|
import { universalFieldUtils, DynamicField } from "@tager/admin-dynamic-field";
|
|
9
9
|
import { compile } from "path-to-regexp";
|
|
@@ -27,7 +27,7 @@ const EN = {
|
|
|
27
27
|
edit: "Edit",
|
|
28
28
|
remove: "Remove",
|
|
29
29
|
postsByCategory: "Posts by category",
|
|
30
|
-
name: "
|
|
30
|
+
name: "Title",
|
|
31
31
|
image: "Image",
|
|
32
32
|
language: "Language",
|
|
33
33
|
languages: "Languages",
|
|
@@ -93,7 +93,12 @@ const EN = {
|
|
|
93
93
|
archiveAt: "Archive at",
|
|
94
94
|
publishAt: "Publish at",
|
|
95
95
|
archiveAtLabel: "Archive at",
|
|
96
|
-
publishAtLabel: "Publish at"
|
|
96
|
+
publishAtLabel: "Publish at",
|
|
97
|
+
sortDateTimeAsc: "Date - Newest to Oldest",
|
|
98
|
+
sortDateTimeDesc: "Date - Oldest to Newest",
|
|
99
|
+
sortTitleAsc: "Title - A-Z",
|
|
100
|
+
sortTitleDesc: "Title - Z-A",
|
|
101
|
+
sortStatus: "Status"
|
|
97
102
|
};
|
|
98
103
|
const RU = {
|
|
99
104
|
yes: "\u0414\u0430",
|
|
@@ -114,7 +119,7 @@ const RU = {
|
|
|
114
119
|
edit: "\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
|
|
115
120
|
remove: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C",
|
|
116
121
|
postsByCategory: "\u041F\u043E\u0441\u0442\u044B \u043F\u043E \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F\u043C",
|
|
117
|
-
name: "\
|
|
122
|
+
name: "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A",
|
|
118
123
|
image: "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435",
|
|
119
124
|
language: "\u042F\u0437\u044B\u043A",
|
|
120
125
|
languages: "\u042F\u0437\u044B\u043A\u0438",
|
|
@@ -180,7 +185,12 @@ const RU = {
|
|
|
180
185
|
archiveAt: "\u041F\u0435\u0440\u0435\u043D\u0435\u0441\u0442\u0438 \u0432 \u0430\u0440\u0445\u0438\u0432 \u0432",
|
|
181
186
|
publishAt: "\u041E\u043F\u0443\u0431\u043B\u0438\u043A\u043E\u0432\u0430\u0442\u044C \u0432",
|
|
182
187
|
archiveAtLabel: "\u041F\u0435\u0440\u0435\u043D\u043E\u0441 \u0432 \u0430\u0440\u0445\u0438\u0432",
|
|
183
|
-
publishAtLabel: "\u041F\u0443\u0431\u043B\u0438\u043A\u0430\u0446\u0438\u044F"
|
|
188
|
+
publishAtLabel: "\u041F\u0443\u0431\u043B\u0438\u043A\u0430\u0446\u0438\u044F",
|
|
189
|
+
sortDateTimeAsc: "\u0414\u0430\u0442\u0435 - \u041F\u043E \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0430\u043D\u0438\u044E",
|
|
190
|
+
sortDateTimeDesc: "\u0414\u0430\u0442\u0435 - \u041F\u043E \u0443\u0431\u044B\u0432\u0430\u043D\u0438\u044E",
|
|
191
|
+
sortTitleAsc: "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0443 - \u0410-\u042F",
|
|
192
|
+
sortTitleDesc: "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0443 - \u042F-\u0410",
|
|
193
|
+
sortStatus: "\u0421\u0442\u0430\u0442\u0443\u0441\u0443"
|
|
184
194
|
};
|
|
185
195
|
function applyTranslations() {
|
|
186
196
|
i18n.addTranslations("en", "blog", EN);
|
|
@@ -239,7 +249,7 @@ function getModuleConfig() {
|
|
|
239
249
|
}
|
|
240
250
|
function useFetchPosts() {
|
|
241
251
|
const [fetchPosts, resource] = useResource({
|
|
242
|
-
fetchResource: () => getPosts({ pageNumber: 1, pageSize: 99999 }),
|
|
252
|
+
fetchResource: () => getPosts({ pageNumber: 1, pageSize: 99999, sort: "status" }),
|
|
243
253
|
initialValue: [],
|
|
244
254
|
resourceName: "Blog posts"
|
|
245
255
|
});
|
|
@@ -521,6 +531,30 @@ function getPostTableColumnDefs(moduleConfig, t) {
|
|
|
521
531
|
];
|
|
522
532
|
return COLUMN_DEFS.filter(notEmpty);
|
|
523
533
|
}
|
|
534
|
+
function getPostTableSortOptions(t) {
|
|
535
|
+
return [
|
|
536
|
+
{
|
|
537
|
+
value: "datetime_desc",
|
|
538
|
+
label: t("blog:sortDateTimeDesc")
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
value: "datetime_asc",
|
|
542
|
+
label: t("blog:sortDateTimeAsc")
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
value: "title_asc",
|
|
546
|
+
label: t("blog:sortTitleAsc")
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
value: "title_desc",
|
|
550
|
+
label: t("blog:sortTitleDesc")
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
value: "status",
|
|
554
|
+
label: t("blog:sortStatus")
|
|
555
|
+
}
|
|
556
|
+
];
|
|
557
|
+
}
|
|
524
558
|
const dateFormat = (date) => date.split("-").reverse().join(".");
|
|
525
559
|
function useAdvancedSearch$1({
|
|
526
560
|
t,
|
|
@@ -773,6 +807,7 @@ const _sfc_main$3 = defineComponent({
|
|
|
773
807
|
languageList,
|
|
774
808
|
statusOptionList
|
|
775
809
|
});
|
|
810
|
+
const sortOptions = getPostTableSortOptions(t);
|
|
776
811
|
const {
|
|
777
812
|
fetchEntityList: fetchPostList,
|
|
778
813
|
isLoading: isPostLoading,
|
|
@@ -782,24 +817,27 @@ const _sfc_main$3 = defineComponent({
|
|
|
782
817
|
handleChange,
|
|
783
818
|
pageNumber,
|
|
784
819
|
pageCount,
|
|
785
|
-
pageSize
|
|
820
|
+
pageSize,
|
|
821
|
+
sort
|
|
786
822
|
} = useDataTable({
|
|
787
823
|
fetchEntityList: (params) => getPosts({
|
|
788
824
|
query: params.searchQuery,
|
|
789
825
|
pageNumber: params.pageNumber,
|
|
790
826
|
pageSize: params.pageSize,
|
|
827
|
+
sort: params.sort || void 0,
|
|
791
828
|
...filterParams.value
|
|
792
829
|
}),
|
|
793
830
|
initialValue: [],
|
|
794
831
|
resourceName: "Blog post list",
|
|
795
|
-
pageSize: 100
|
|
832
|
+
pageSize: 100,
|
|
833
|
+
defaultSort: sortOptions[0].value
|
|
796
834
|
});
|
|
797
835
|
watch(filterParams, () => {
|
|
798
836
|
if (isModuleConfigLoading.value || isCategoryListLoading.value) {
|
|
799
837
|
return;
|
|
800
838
|
}
|
|
801
839
|
const newQuery = {
|
|
802
|
-
...pick(route.query, ["query", "pageNumber"]),
|
|
840
|
+
...pick(route.query, ["query", "pageNumber", "sort"]),
|
|
803
841
|
...filterParams.value
|
|
804
842
|
};
|
|
805
843
|
if (!isEqual(route.query, newQuery)) {
|
|
@@ -842,6 +880,8 @@ const _sfc_main$3 = defineComponent({
|
|
|
842
880
|
t,
|
|
843
881
|
formatDateTime,
|
|
844
882
|
columnDefs,
|
|
883
|
+
sortOptions,
|
|
884
|
+
sortValue: sort,
|
|
845
885
|
rowData: displayedPostList,
|
|
846
886
|
handleResourceDelete,
|
|
847
887
|
isDeleting,
|
|
@@ -909,6 +949,7 @@ function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
909
949
|
pageSize: _ctx.pageSize,
|
|
910
950
|
disabled: _ctx.isRowDataLoading
|
|
911
951
|
},
|
|
952
|
+
sort: { options: _ctx.sortOptions, value: _ctx.sortValue },
|
|
912
953
|
"data-table": "blog-post",
|
|
913
954
|
onChange: _ctx.handleChange
|
|
914
955
|
}, {
|
|
@@ -1019,12 +1060,12 @@ function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
1019
1060
|
}, 1032, ["title", "disabled", "onClick"])
|
|
1020
1061
|
]),
|
|
1021
1062
|
_: 1
|
|
1022
|
-
}, 8, ["column-defs", "row-data", "loading", "error-message", "search-query", "pagination", "onChange"])
|
|
1063
|
+
}, 8, ["column-defs", "row-data", "loading", "error-message", "search-query", "pagination", "sort", "onChange"])
|
|
1023
1064
|
]),
|
|
1024
1065
|
_: 1
|
|
1025
1066
|
}, 8, ["title", "header-buttons"]);
|
|
1026
1067
|
}
|
|
1027
|
-
var BlogPostList = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__scopeId", "data-v-
|
|
1068
|
+
var BlogPostList = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__scopeId", "data-v-02162423"]]);
|
|
1028
1069
|
var PostForm_vue_vue_type_style_index_0_scoped_true_lang = "";
|
|
1029
1070
|
const _sfc_main$2 = defineComponent({
|
|
1030
1071
|
name: "BlogPostForm",
|
|
@@ -1090,7 +1131,7 @@ const _sfc_main$2 = defineComponent({
|
|
|
1090
1131
|
return relatedPost.id !== ((_a2 = post.value) == null ? void 0 : _a2.id);
|
|
1091
1132
|
}).map((post2) => ({
|
|
1092
1133
|
value: post2.id,
|
|
1093
|
-
label: post2.title
|
|
1134
|
+
label: (post2.status === "ARCHIVED" ? `${t("blog:statusArchived")} - ` : post2.status === "DRAFT" ? `${t("blog:statusDraft")} - ` : post2.datetime ? formatDate(new Date(post2.datetime)) + " - " : "") + post2.title
|
|
1094
1135
|
}))
|
|
1095
1136
|
);
|
|
1096
1137
|
const isSubmitting = ref(false);
|
|
@@ -1555,7 +1596,7 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
1555
1596
|
_: 1
|
|
1556
1597
|
}, 8, ["title", "is-content-loading", "header-buttons", "footer"]);
|
|
1557
1598
|
}
|
|
1558
|
-
var BlogPostForm = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-
|
|
1599
|
+
var BlogPostForm = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-22282a12"]]);
|
|
1559
1600
|
function convertCategoryList(categoryList, languages) {
|
|
1560
1601
|
return categoryList.map((category) => {
|
|
1561
1602
|
if (languages.length > 0) {
|
|
@@ -1906,7 +1947,8 @@ function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
1906
1947
|
]),
|
|
1907
1948
|
_: 1
|
|
1908
1949
|
}, 8, ["tags", "onClick:tag"])
|
|
1909
|
-
])
|
|
1950
|
+
]),
|
|
1951
|
+
key: "0"
|
|
1910
1952
|
} : void 0
|
|
1911
1953
|
]), 1032, ["column-defs", "row-data", "loading", "error-message", "search-query", "pagination", "onChange"])
|
|
1912
1954
|
]),
|
package/dist/locales/en.d.ts
CHANGED
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.filters[data-v-
|
|
1
|
+
.filters[data-v-02162423]{margin-bottom:1rem;width:100%;display:grid;gap:1rem;grid-template-columns:1fr 1fr}.filters[data-v-02162423]:not(:first-child){margin-top:1rem}.status span[data-v-02162423]{display:block}.status span[data-v-02162423]:nth-child(2){display:block;margin-top:5px;font-size:.8rem}.status span:nth-child(2) i[data-v-02162423]{display:block}.status[data-v-22282a12]{display:flex;align-items:flex-end}.status-value[data-v-22282a12]{flex-basis:50%}.status-checkbox[data-v-22282a12]{margin-left:20px;padding-bottom:8px}.status-date[data-v-22282a12]{margin-left:20px;flex:1 1 1px}
|
|
@@ -49,5 +49,5 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
49
49
|
handleSeoFieldGroupChange: ({ title, description, keywords, image, }: SeoChangeEvent) => void;
|
|
50
50
|
tabList: import("vue").ComputedRef<TabType[]>;
|
|
51
51
|
selectedTabId: import("vue").Ref<string>;
|
|
52
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin,
|
|
52
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
53
53
|
export default _default;
|
|
@@ -22,5 +22,5 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
22
22
|
languageOptionList: import("vue").ComputedRef<import("@tager/admin-ui").OptionType<string>[]>;
|
|
23
23
|
tags: import("vue").ComputedRef<import("@tager/admin-ui").FilterTagType[]>;
|
|
24
24
|
tagRemovalHandler: (event: import("@tager/admin-ui").FilterTagType) => void;
|
|
25
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin,
|
|
25
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
26
26
|
export default _default;
|
|
@@ -2764,5 +2764,5 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
2764
2764
|
urlAliasTemplate: import("vue").ComputedRef<string>;
|
|
2765
2765
|
handleCategoriesChange: (selectedOptions: OptionType<number>[]) => void;
|
|
2766
2766
|
statusOptions: import("vue").ComputedRef<OptionType<string>[]>;
|
|
2767
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin,
|
|
2767
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
2768
2768
|
export default _default;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
2
|
import { Nullable } from '@tager/admin-services';
|
|
3
|
-
import { ColumnDefinition } from '@tager/admin-ui';
|
|
3
|
+
import { ColumnDefinition, OptionType } from '@tager/admin-ui';
|
|
4
4
|
import { Category, ModuleConfig, Language, PostShort } from '../../../typings/model';
|
|
5
5
|
export declare function convertPostList(postList: PostShort[], selectedCategory: Nullable<Category>, languages: Language[]): PostShort[];
|
|
6
6
|
export declare function getStatuses(t: TFunction): Record<string, string>;
|
|
7
7
|
export declare function getPostTableColumnDefs(moduleConfig: Nullable<ModuleConfig>, t: TFunction): ColumnDefinition<PostShort>[];
|
|
8
|
+
export declare function getPostTableSortOptions(t: TFunction): Array<OptionType>;
|
|
@@ -7,6 +7,8 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
7
7
|
t: import("i18next").TFunction;
|
|
8
8
|
formatDateTime: typeof formatDateTime;
|
|
9
9
|
columnDefs: import("vue").ComputedRef<ColumnDefinition<PostShort>[]>;
|
|
10
|
+
sortOptions: OptionType<string>[];
|
|
11
|
+
sortValue: import("vue").Ref<Nullable<string>>;
|
|
10
12
|
rowData: import("vue").ComputedRef<PostShort[]>;
|
|
11
13
|
handleResourceDelete: (entityId: string | number) => void;
|
|
12
14
|
isDeleting: (entityId: string | number) => boolean;
|
|
@@ -34,5 +36,5 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
34
36
|
getStatuses: typeof getStatuses;
|
|
35
37
|
handleResourceClone: (entityId: string | number) => void;
|
|
36
38
|
isBusy: (postId: number) => boolean;
|
|
37
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin,
|
|
39
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>;
|
|
38
40
|
export default _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tager/admin-blog",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.umd.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"@babel/eslint-parser": "^7.17.0",
|
|
35
35
|
"@rushstack/eslint-patch": "^1.1.2",
|
|
36
36
|
"@tager/admin-dynamic-field": "^1.0.0",
|
|
37
|
-
"@tager/admin-layout": "^1.0.
|
|
38
|
-
"@tager/admin-services": "^1.
|
|
39
|
-
"@tager/admin-ui": "^1.0.
|
|
37
|
+
"@tager/admin-layout": "^1.0.7",
|
|
38
|
+
"@tager/admin-services": "^1.1.3",
|
|
39
|
+
"@tager/admin-ui": "^1.0.15",
|
|
40
40
|
"@types/filesize": "^5.0.0",
|
|
41
41
|
"@types/lodash.isequal": "^4.5.6",
|
|
42
42
|
"@types/lodash.pick": "^4.4.7",
|
|
@@ -7,7 +7,8 @@ import { getPosts } from '../services/requests';
|
|
|
7
7
|
|
|
8
8
|
export function useFetchPosts(): ResourceRef<PostShort[]> {
|
|
9
9
|
const [fetchPosts, resource] = useResource<PostShort[]>({
|
|
10
|
-
fetchResource: () =>
|
|
10
|
+
fetchResource: () =>
|
|
11
|
+
getPosts({ pageNumber: 1, pageSize: 99999, sort: 'status' }),
|
|
11
12
|
initialValue: [],
|
|
12
13
|
resourceName: 'Blog posts',
|
|
13
14
|
});
|
package/src/locales/en.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const EN = {
|
|
|
17
17
|
edit: 'Edit',
|
|
18
18
|
remove: 'Remove',
|
|
19
19
|
postsByCategory: 'Posts by category',
|
|
20
|
-
name: '
|
|
20
|
+
name: 'Title',
|
|
21
21
|
image: 'Image',
|
|
22
22
|
language: 'Language',
|
|
23
23
|
languages: 'Languages',
|
|
@@ -89,4 +89,10 @@ export const EN = {
|
|
|
89
89
|
publishAt: 'Publish at',
|
|
90
90
|
archiveAtLabel: 'Archive at',
|
|
91
91
|
publishAtLabel: 'Publish at',
|
|
92
|
+
|
|
93
|
+
sortDateTimeAsc: 'Date - Newest to Oldest',
|
|
94
|
+
sortDateTimeDesc: 'Date - Oldest to Newest',
|
|
95
|
+
sortTitleAsc: 'Title - A-Z',
|
|
96
|
+
sortTitleDesc: 'Title - Z-A',
|
|
97
|
+
sortStatus: 'Status',
|
|
92
98
|
};
|
package/src/locales/ru.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const RU: typeof EN = {
|
|
|
19
19
|
edit: 'Редактировать',
|
|
20
20
|
remove: 'Удалить',
|
|
21
21
|
postsByCategory: 'Посты по категориям',
|
|
22
|
-
name: '
|
|
22
|
+
name: 'Заголовок',
|
|
23
23
|
image: 'Изображение',
|
|
24
24
|
language: 'Язык',
|
|
25
25
|
languages: 'Языки',
|
|
@@ -91,6 +91,12 @@ export const RU: typeof EN = {
|
|
|
91
91
|
publishAt: 'Опубликовать в',
|
|
92
92
|
archiveAtLabel: 'Перенос в архив',
|
|
93
93
|
publishAtLabel: 'Публикация',
|
|
94
|
+
|
|
95
|
+
sortDateTimeAsc: 'Дате - По возрастанию',
|
|
96
|
+
sortDateTimeDesc: 'Дате - По убыванию',
|
|
97
|
+
sortTitleAsc: 'Заголовку - А-Я',
|
|
98
|
+
sortTitleDesc: 'Заголовку - Я-А',
|
|
99
|
+
sortStatus: 'Статусу',
|
|
94
100
|
};
|
|
95
101
|
|
|
96
102
|
export default RU;
|
package/src/services/requests.ts
CHANGED
|
@@ -254,7 +254,7 @@ import {
|
|
|
254
254
|
FormFieldFileInput,
|
|
255
255
|
FormFieldMultiSelect,
|
|
256
256
|
SeoFieldGroup,
|
|
257
|
-
TabList,
|
|
257
|
+
TabList, formatDateTime, formatDate,
|
|
258
258
|
} from '@tager/admin-ui';
|
|
259
259
|
import { DynamicField } from '@tager/admin-dynamic-field';
|
|
260
260
|
import { Page } from '@tager/admin-layout';
|
|
@@ -365,9 +365,14 @@ export default defineComponent({
|
|
|
365
365
|
.filter((relatedPost) => relatedPost.id !== post.value?.id)
|
|
366
366
|
.map((post) => ({
|
|
367
367
|
value: post.id,
|
|
368
|
-
label:
|
|
368
|
+
label:
|
|
369
|
+
(post.status === 'ARCHIVED'
|
|
370
|
+
? `${t('blog:statusArchived')} - `
|
|
371
|
+
: post.status === 'DRAFT'
|
|
372
|
+
? `${t('blog:statusDraft')} - `
|
|
373
|
+
: (post.datetime ? formatDate(new Date(post.datetime)) + ' - ' : '')) + post.title,
|
|
369
374
|
}))
|
|
370
|
-
)
|
|
375
|
+
)
|
|
371
376
|
|
|
372
377
|
/** Form State **/
|
|
373
378
|
const isSubmitting = ref<boolean>(false);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
2
|
|
|
3
3
|
import { notEmpty, Nullable } from '@tager/admin-services';
|
|
4
|
-
import { ColumnDefinition } from '@tager/admin-ui';
|
|
4
|
+
import { ColumnDefinition, OptionType } from '@tager/admin-ui';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
Category,
|
|
@@ -122,3 +122,28 @@ export function getPostTableColumnDefs(
|
|
|
122
122
|
|
|
123
123
|
return COLUMN_DEFS.filter(notEmpty);
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
export function getPostTableSortOptions(t: TFunction): Array<OptionType> {
|
|
127
|
+
return [
|
|
128
|
+
{
|
|
129
|
+
value: 'datetime_desc',
|
|
130
|
+
label: t('blog:sortDateTimeDesc'),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
value: 'datetime_asc',
|
|
134
|
+
label: t('blog:sortDateTimeAsc'),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
value: 'title_asc',
|
|
138
|
+
label: t('blog:sortTitleAsc'),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
value: 'title_desc',
|
|
142
|
+
label: t('blog:sortTitleDesc'),
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
value: 'status',
|
|
146
|
+
label: t('blog:sortStatus'),
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Page
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
:title="pageTitle"
|
|
4
|
+
:header-buttons="[
|
|
5
5
|
{
|
|
6
6
|
text: $i18n.t('blog:newPost'),
|
|
7
7
|
href: getBlogPostFormUrl({ postId: 'create' }),
|
|
@@ -10,66 +10,67 @@
|
|
|
10
10
|
>
|
|
11
11
|
<template #content>
|
|
12
12
|
<DataTable
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
:column-defs="columnDefs"
|
|
14
|
+
:row-data="rowData"
|
|
15
|
+
:loading="isRowDataLoading"
|
|
16
|
+
:error-message="errorMessage"
|
|
17
|
+
:search-query="searchQuery"
|
|
18
|
+
:pagination="{
|
|
19
19
|
pageNumber,
|
|
20
20
|
pageCount,
|
|
21
21
|
pageSize,
|
|
22
22
|
disabled: isRowDataLoading,
|
|
23
23
|
}"
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
:sort="{ options: sortOptions, value: sortValue }"
|
|
25
|
+
data-table="blog-post"
|
|
26
|
+
@change="handleChange"
|
|
26
27
|
>
|
|
27
28
|
<template #filters>
|
|
28
29
|
<AdvancedSearch :tags="tags" @click:tag="tagRemovalHandler">
|
|
29
30
|
<div class="filters">
|
|
30
31
|
<FormFieldMultiSelect
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
v-model:selected-options="categoryFilter"
|
|
33
|
+
:options="categoryOptionList"
|
|
34
|
+
name="categoryFilter"
|
|
35
|
+
:searchable="true"
|
|
36
|
+
:label="$i18n.t('blog:category')"
|
|
37
|
+
class="filter"
|
|
37
38
|
/>
|
|
38
39
|
|
|
39
40
|
<FormFieldMultiSelect
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
v-if="isLangSpecific"
|
|
42
|
+
v-model:selected-options="languageFilter"
|
|
43
|
+
:options="languageOptionList"
|
|
44
|
+
name="languageFilter"
|
|
45
|
+
:searchable="true"
|
|
46
|
+
:label="$i18n.t('blog:language')"
|
|
47
|
+
class="filter"
|
|
47
48
|
/>
|
|
48
49
|
<FormFieldMultiSelect
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
v-model:selected-options="statusFilter"
|
|
51
|
+
:options="statusOptionList"
|
|
52
|
+
name="statusFilter"
|
|
53
|
+
:searchable="true"
|
|
54
|
+
:label="$i18n.t('blog:status')"
|
|
55
|
+
class="filter"
|
|
55
56
|
/>
|
|
56
57
|
</div>
|
|
57
58
|
|
|
58
59
|
<div class="filters">
|
|
59
60
|
<FormField
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
v-model:value="fromDateFilter"
|
|
62
|
+
:label="$i18n.t('blog:dateOfPublicationFrom')"
|
|
63
|
+
name="fromDateFilter"
|
|
64
|
+
type="date"
|
|
65
|
+
:max="toDateFilter"
|
|
65
66
|
/>
|
|
66
67
|
|
|
67
68
|
<FormField
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
v-model:value="toDateFilter"
|
|
70
|
+
:label="$i18n.t('blog:dateOfPublicationTo')"
|
|
71
|
+
name="toDateFilter"
|
|
72
|
+
type="date"
|
|
73
|
+
:min="fromDateFilter"
|
|
73
74
|
/>
|
|
74
75
|
</div>
|
|
75
76
|
</AdvancedSearch>
|
|
@@ -78,10 +79,12 @@
|
|
|
78
79
|
<template #cell(status)="{ row }">
|
|
79
80
|
<div class="status">
|
|
80
81
|
<span>{{ getStatuses($i18n.t)[row.status] }}</span>
|
|
82
|
+
|
|
81
83
|
<span v-if="row.status === 'PUBLISHED' && row.archiveAt">
|
|
82
84
|
{{ $i18n.t('blog:archiveAtLabel') }}:
|
|
83
85
|
<i>{{ formatDateTime(new Date(row.archiveAt), true) }}</i>
|
|
84
86
|
</span>
|
|
87
|
+
|
|
85
88
|
<span v-if="row.status === 'DRAFT' && row.publishAt">
|
|
86
89
|
{{ $i18n.t('blog:publishAtLabel') }}:
|
|
87
90
|
<i>{{ formatDateTime(new Date(row.publishAt), true) }}</i>
|
|
@@ -91,30 +94,30 @@
|
|
|
91
94
|
|
|
92
95
|
<template #cell(actions)="{ row }">
|
|
93
96
|
<BaseButton
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
variant="icon"
|
|
98
|
+
:title="$i18n.t('blog:edit')"
|
|
99
|
+
:disabled="isBusy(row.id)"
|
|
100
|
+
:href="getBlogPostFormUrl({ postId: row.id })"
|
|
98
101
|
>
|
|
99
|
-
<EditIcon
|
|
102
|
+
<EditIcon/>
|
|
100
103
|
</BaseButton>
|
|
101
104
|
|
|
102
105
|
<BaseButton
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
variant="icon"
|
|
107
|
+
:title="$i18n.t('blog:clone')"
|
|
108
|
+
:disabled="isBusy(row.id)"
|
|
109
|
+
@click="handleResourceClone(row.id)"
|
|
107
110
|
>
|
|
108
|
-
<ContentCopyIcon
|
|
111
|
+
<ContentCopyIcon/>
|
|
109
112
|
</BaseButton>
|
|
110
113
|
|
|
111
114
|
<BaseButton
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
variant="icon"
|
|
116
|
+
:title="$i18n.t('blog:remove')"
|
|
117
|
+
:disabled="isBusy(row.id)"
|
|
118
|
+
@click="handleResourceDelete(row.id)"
|
|
116
119
|
>
|
|
117
|
-
<DeleteIcon
|
|
120
|
+
<DeleteIcon/>
|
|
118
121
|
</BaseButton>
|
|
119
122
|
</template>
|
|
120
123
|
</DataTable>
|
|
@@ -123,10 +126,10 @@
|
|
|
123
126
|
</template>
|
|
124
127
|
|
|
125
128
|
<script lang="ts">
|
|
126
|
-
import {
|
|
129
|
+
import {computed, defineComponent, watch} from 'vue';
|
|
127
130
|
import isEqual from 'lodash.isequal';
|
|
128
131
|
import pick from 'lodash.pick';
|
|
129
|
-
import {
|
|
132
|
+
import {useRoute, useRouter} from 'vue-router';
|
|
130
133
|
|
|
131
134
|
import {
|
|
132
135
|
ColumnDefinition,
|
|
@@ -148,29 +151,30 @@ import {
|
|
|
148
151
|
useResourceClone,
|
|
149
152
|
useResourceDelete,
|
|
150
153
|
} from '@tager/admin-services';
|
|
151
|
-
import {
|
|
154
|
+
import {Page} from '@tager/admin-layout';
|
|
152
155
|
|
|
153
|
-
import {
|
|
156
|
+
import {useFetchModuleConfig, useFetchCategories} from '../../../hooks';
|
|
154
157
|
import {
|
|
155
158
|
clonePost,
|
|
156
159
|
deleteBlogPost,
|
|
157
160
|
getPosts,
|
|
158
161
|
} from '../../../services/requests';
|
|
159
|
-
import {
|
|
162
|
+
import {getBlogPostFormUrl} from '../../../utils/paths';
|
|
160
163
|
import {
|
|
161
164
|
Category,
|
|
162
165
|
Language,
|
|
163
166
|
PostFull,
|
|
164
167
|
PostShort,
|
|
165
168
|
} from '../../../typings/model';
|
|
166
|
-
import {
|
|
169
|
+
import {getStatusOptions} from '../PostForm/PostForm.helpers';
|
|
167
170
|
|
|
168
171
|
import {
|
|
169
172
|
convertPostList,
|
|
170
173
|
getPostTableColumnDefs,
|
|
174
|
+
getPostTableSortOptions,
|
|
171
175
|
getStatuses,
|
|
172
176
|
} from './PostList.helpers';
|
|
173
|
-
import {
|
|
177
|
+
import {useAdvancedSearch} from './hooks';
|
|
174
178
|
|
|
175
179
|
export default defineComponent({
|
|
176
180
|
name: 'PostList',
|
|
@@ -186,48 +190,48 @@ export default defineComponent({
|
|
|
186
190
|
DataTable,
|
|
187
191
|
},
|
|
188
192
|
setup() {
|
|
189
|
-
const {
|
|
193
|
+
const {t} = useI18n();
|
|
190
194
|
const route = useRoute();
|
|
191
195
|
const router = useRouter();
|
|
192
196
|
|
|
193
197
|
const categoryId = computed<string>(() => {
|
|
194
198
|
return Array.isArray(route.query.category)
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
? (route.query.category[0] as string) ?? ''
|
|
200
|
+
: (route.query.category as string);
|
|
197
201
|
});
|
|
198
202
|
|
|
199
203
|
const statusOptionList = computed<OptionType[]>(() => getStatusOptions(t));
|
|
200
204
|
|
|
201
205
|
/** Fetch module config **/
|
|
202
206
|
|
|
203
|
-
const {
|
|
204
|
-
|
|
207
|
+
const {data: moduleConfig, loading: isModuleConfigLoading} =
|
|
208
|
+
useFetchModuleConfig();
|
|
205
209
|
|
|
206
210
|
const languageList = computed<Language[]>(
|
|
207
|
-
|
|
211
|
+
() => moduleConfig.value?.languages ?? []
|
|
208
212
|
);
|
|
209
213
|
|
|
210
214
|
/** Fetch category list **/
|
|
211
215
|
|
|
212
|
-
const {
|
|
213
|
-
|
|
216
|
+
const {data: categoryList, loading: isCategoryListLoading} =
|
|
217
|
+
useFetchCategories();
|
|
214
218
|
|
|
215
219
|
const selectedCategory = computed<Nullable<Category>>(() =>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
({
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
categoryId.value
|
|
221
|
+
? categoryList.value.find(
|
|
222
|
+
({id}) => String(id) === String(categoryId.value)
|
|
223
|
+
) ?? null
|
|
224
|
+
: null
|
|
221
225
|
);
|
|
222
226
|
|
|
223
227
|
const pageTitle = computed<string>(() => {
|
|
224
228
|
return selectedCategory.value
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
? `${t('blog:postsByCategory')} "${selectedCategory.value.name}"`
|
|
230
|
+
: t('blog:posts');
|
|
227
231
|
});
|
|
228
232
|
|
|
229
233
|
const isLangSpecific = computed<boolean>(
|
|
230
|
-
|
|
234
|
+
() => languageOptionList.value.length > 1
|
|
231
235
|
);
|
|
232
236
|
|
|
233
237
|
/** Advanced search **/
|
|
@@ -251,6 +255,8 @@ export default defineComponent({
|
|
|
251
255
|
statusOptionList,
|
|
252
256
|
});
|
|
253
257
|
|
|
258
|
+
const sortOptions = getPostTableSortOptions(t);
|
|
259
|
+
|
|
254
260
|
/** Fetch Post list **/
|
|
255
261
|
|
|
256
262
|
const {
|
|
@@ -263,17 +269,20 @@ export default defineComponent({
|
|
|
263
269
|
pageNumber,
|
|
264
270
|
pageCount,
|
|
265
271
|
pageSize,
|
|
272
|
+
sort,
|
|
266
273
|
} = useDataTable<PostShort>({
|
|
267
274
|
fetchEntityList: (params) =>
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
getPosts({
|
|
276
|
+
query: params.searchQuery,
|
|
277
|
+
pageNumber: params.pageNumber,
|
|
278
|
+
pageSize: params.pageSize,
|
|
279
|
+
sort: params.sort || undefined,
|
|
280
|
+
...filterParams.value,
|
|
281
|
+
}),
|
|
274
282
|
initialValue: [],
|
|
275
283
|
resourceName: 'Blog post list',
|
|
276
284
|
pageSize: 100,
|
|
285
|
+
defaultSort: sortOptions[0].value,
|
|
277
286
|
});
|
|
278
287
|
|
|
279
288
|
watch(filterParams, () => {
|
|
@@ -282,38 +291,38 @@ export default defineComponent({
|
|
|
282
291
|
}
|
|
283
292
|
|
|
284
293
|
const newQuery = {
|
|
285
|
-
...pick(route.query, ['query', 'pageNumber']),
|
|
294
|
+
...pick(route.query, ['query', 'pageNumber', 'sort']),
|
|
286
295
|
...filterParams.value,
|
|
287
296
|
};
|
|
288
297
|
|
|
289
298
|
if (!isEqual(route.query, newQuery)) {
|
|
290
|
-
router.replace({
|
|
299
|
+
router.replace({query: newQuery});
|
|
291
300
|
fetchPostList();
|
|
292
301
|
}
|
|
293
302
|
});
|
|
294
303
|
|
|
295
304
|
const displayedPostList = computed<PostShort[]>(() =>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
305
|
+
convertPostList(
|
|
306
|
+
postList.value,
|
|
307
|
+
selectedCategory.value,
|
|
308
|
+
moduleConfig.value?.languages ?? []
|
|
309
|
+
)
|
|
301
310
|
);
|
|
302
311
|
|
|
303
312
|
const isRowDataLoading = computed<boolean>(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
313
|
+
() =>
|
|
314
|
+
isCategoryListLoading.value ||
|
|
315
|
+
isPostLoading.value ||
|
|
316
|
+
isModuleConfigLoading.value
|
|
308
317
|
);
|
|
309
318
|
|
|
310
|
-
const {
|
|
319
|
+
const {isDeleting, handleResourceDelete} = useResourceDelete({
|
|
311
320
|
deleteResource: deleteBlogPost,
|
|
312
321
|
resourceName: 'Post',
|
|
313
322
|
onSuccess: fetchPostList,
|
|
314
323
|
});
|
|
315
324
|
|
|
316
|
-
const {
|
|
325
|
+
const {isCloning, handleResourceClone} = useResourceClone({
|
|
317
326
|
cloneResource: clonePost,
|
|
318
327
|
confirmMessage: t('blog:cloneConfirm'),
|
|
319
328
|
successMessage: t('blog:cloneSuccess'),
|
|
@@ -326,13 +335,15 @@ export default defineComponent({
|
|
|
326
335
|
}
|
|
327
336
|
|
|
328
337
|
const columnDefs = computed<ColumnDefinition<PostShort>[]>(() =>
|
|
329
|
-
|
|
338
|
+
getPostTableColumnDefs(moduleConfig.value, t)
|
|
330
339
|
);
|
|
331
340
|
|
|
332
341
|
return {
|
|
333
342
|
t,
|
|
334
343
|
formatDateTime,
|
|
335
344
|
columnDefs,
|
|
345
|
+
sortOptions,
|
|
346
|
+
sortValue: sort,
|
|
336
347
|
rowData: displayedPostList,
|
|
337
348
|
handleResourceDelete,
|
|
338
349
|
isDeleting,
|
|
@@ -383,7 +394,7 @@ export default defineComponent({
|
|
|
383
394
|
}
|
|
384
395
|
|
|
385
396
|
.status {
|
|
386
|
-
span
|
|
397
|
+
span {
|
|
387
398
|
display: block;
|
|
388
399
|
}
|
|
389
400
|
|