Skip to content

Commit 0cc0faa

Browse files
committed
feat: Home Custom Content and tools filters
Fix CorentinTh#1538 and part of CorentinTh#395
1 parent 1a0ab17 commit 0cc0faa

10 files changed

Lines changed: 109 additions & 13 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ services:
3333
- 8080:8080
3434
```
3535
36+
## Filter tools and add home custom content
37+
38+
You can add custom content in Home page by mounting a `home.custom.md` in `/app`.
39+
40+
You can filter available tools by mounting `tools-filter.json` in `/app`. It can contains the following filtering regex:
41+
```json
42+
{
43+
"excludeCategoryFilterRegex": "",
44+
"includeCategoryFilterRegex": "",
45+
"excludeToolsFilterRegex": "",
46+
"includeToolsFilterRegex": ""
47+
}
48+
```
49+
Category matches on category (English) names ; Tools matches on tools path/url.
50+
51+
See (docker-tools-filter-and-home-content)[https://github.com/sharevb/it-tools]
52+
3653
## To build using a custom folder:
3754

3855
```

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ declare module '@vue/runtime-core' {
279279
NH1: typeof import('naive-ui')['NH1']
280280
NH3: typeof import('naive-ui')['NH3']
281281
NIcon: typeof import('naive-ui')['NIcon']
282+
NInputGroup: typeof import('naive-ui')['NInputGroup']
283+
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
282284
NInputNumber: typeof import('naive-ui')['NInputNumber']
283285
NLayout: typeof import('naive-ui')['NLayout']
284286
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
services:
2+
it-tools:
3+
container_name: it-tools
4+
image: sharevb/it-tools:latest
5+
pull_policy: always
6+
restart: unless-stopped
7+
volumes:
8+
- ./tools-filter.json:/app/tools-filter.json
9+
- ./home.custom.md:/app/home.custom.md
10+
ports:
11+
- 8080:8080
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"excludeCategoryFilterRegex": "^(Gaming|Maths|Physics)$",
3+
"includeCategoryFilterRegex": "",
4+
"excludeToolsFilterRegex": "ai-prompt-splitter",
5+
"includeToolsFilterRegex": "illuminance-converter"
6+
}

public/home.custom.md

Whitespace-only changes.

public/tools-filter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

src/pages/Home.page.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,24 @@ import { IconDragDrop, IconFileDescription, IconHeart } from '@tabler/icons-vue'
33
import { useHead } from '@vueuse/head';
44
import { computed } from 'vue';
55
import Draggable from 'vuedraggable';
6+
import VueMarkdown from 'vue-markdown-render';
67
import ColoredCard from '../components/ColoredCard.vue';
78
import ToolCard from '../components/ToolCard.vue';
89
import { useToolStore } from '@/tools/tools.store';
910
import { config } from '@/config';
1011
12+
const base = import.meta.env.BASE_URL ?? '/';
13+
const homeCustomMarkdown = computedAsync(async () => {
14+
try {
15+
const remoteCustomHomeMarkdownResponse = await fetch(`${base}home.custom.md`);
16+
if (remoteCustomHomeMarkdownResponse.ok) {
17+
return await remoteCustomHomeMarkdownResponse.text();
18+
}
19+
}
20+
catch {}
21+
return '';
22+
});
23+
1124
const toolStore = useToolStore();
1225
1326
useHead({ title: 'IT Tools - Handy online tools for developers' });
@@ -87,6 +100,10 @@ function onUpdateFavoriteTools() {
87100
</div>
88101
</div>
89102

103+
<div v-if="homeCustomMarkdown">
104+
<VueMarkdown :source="homeCustomMarkdown" />
105+
</div>
106+
90107
<h3 class="mb-5px mt-25px font-500 text-neutral-400">
91108
{{ $t('home.categories.allTools') }}
92109
</h3>

src/tools/coin-flipper/coin-flipper.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const [coinFlip, refreshCoinFlip] = computedRefreshable(() => ({
1212
<div>
1313
<Transition name="bounce" mode="out-in">
1414
<div :key="coinFlip.dt" flex items-center justify-center>
15-
<div flex items-center justify-center rounded-full outline style="width: 5em; height: 5em" mt-1 mb-1>
15+
<div style="width: 5em; height: 5em" mb-1 mt-1 flex items-center justify-center rounded-full outline>
1616
{{ coinFlip.coin }}
1717
</div>
1818
</div>

src/tools/tools.store.ts

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,60 @@ import { type MaybeRef, get, useStorage } from '@vueuse/core';
22
import { defineStore } from 'pinia';
33
import type { Ref } from 'vue';
44
import _ from 'lodash';
5-
import type { Tool, ToolCategory, ToolWithCategory } from './tools.types';
5+
import type { Tool, ToolCategory, ToolWithCategory, ToolsFilter } from './tools.types';
66
import { tools as allTools } from './index';
77

8+
const base = import.meta.env.BASE_URL ?? '/';
9+
let filterConfig: ToolsFilter = {};
10+
try {
11+
const remoteConfigResponse = await fetch(`${base}tools-filter.json`);
12+
if (remoteConfigResponse.ok) {
13+
filterConfig = (await remoteConfigResponse.json()) as ToolsFilter;
14+
}
15+
}
16+
catch {}
17+
818
export const useToolStore = defineStore('tools', () => {
919
const favoriteToolsName = useStorage('favoriteToolsName', []) as Ref<string[]>;
1020
const { t } = useI18n();
1121

12-
const tools = computed<ToolWithCategory[]>(() => allTools.map((tool) => {
13-
const toolI18nKey = tool.path.replace(/\//g, '');
14-
const category = tool.category || 'Development';
22+
const makeRegExp = (regex: string | undefined) => regex ? new RegExp(regex, 'i') : null;
23+
const filters = {
24+
excludeCategoryFilterRegex: makeRegExp(filterConfig.excludeCategoryFilterRegex),
25+
includeCategoryFilterRegex: makeRegExp(filterConfig.includeCategoryFilterRegex),
26+
excludeToolsFilterRegex: makeRegExp(filterConfig.excludeToolsFilterRegex),
27+
includeToolsFilterRegex: makeRegExp(filterConfig.includeToolsFilterRegex),
28+
};
29+
30+
const tools = computed<ToolWithCategory[]>(() => allTools
31+
.filter((tool) => {
32+
const category = tool.category || 'Development';
33+
if (filters.includeToolsFilterRegex?.test(tool.path)) {
34+
return true;
35+
}
36+
if (filters.includeCategoryFilterRegex?.test(category)) {
37+
return true;
38+
}
39+
if (filters.excludeToolsFilterRegex?.test(tool.path)) {
40+
return false;
41+
}
42+
if (filters.excludeCategoryFilterRegex?.test(category)) {
43+
return false;
44+
}
45+
return true;
46+
})
47+
.map((tool) => {
48+
const toolI18nKey = tool.path.replace(/\//g, '');
49+
const category = tool.category || 'Development';
1550

16-
return ({
17-
...tool,
18-
path: tool.path,
19-
name: t(`tools.${toolI18nKey}.title`, tool.name),
20-
description: t(`tools.${toolI18nKey}.description`, tool.description),
21-
category: t(`tools.categories.${category.toLowerCase()}`, category),
22-
});
23-
}));
51+
return ({
52+
...tool,
53+
path: tool.path,
54+
name: t(`tools.${toolI18nKey}.title`, tool.name),
55+
description: t(`tools.${toolI18nKey}.description`, tool.description),
56+
category: t(`tools.categories.${category.toLowerCase()}`, category),
57+
});
58+
}));
2459

2560
const toolsByCategory = computed<ToolCategory[]>(() => {
2661
return _.chain(tools.value)

src/tools/tools.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,11 @@ export interface ToolCategory {
2020
components: Tool[]
2121
}
2222

23+
export interface ToolsFilter {
24+
excludeCategoryFilterRegex?: string
25+
includeCategoryFilterRegex?: string
26+
excludeToolsFilterRegex?: string
27+
includeToolsFilterRegex?: string
28+
}
29+
2330
export type ToolWithCategory = Tool & { category: string };

0 commit comments

Comments
 (0)