mirror of
				https://github.com/CorentinTh/it-tools.git
				synced 2025-11-03 18:37:00 -05:00 
			
		
		
		
	feat(tools): added favorite tool handling
This commit is contained in:
		
							parent
							
								
									8d09086e78
								
							
						
					
					
						commit
						4cd809bd0c
					
				
							
								
								
									
										40
									
								
								src/components/FavoriteButton.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/components/FavoriteButton.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <n-tooltip trigger="hover">
 | 
				
			||||||
 | 
					    <template #trigger>
 | 
				
			||||||
 | 
					      <n-button circle quaternary :type="buttonType" :style="{ opacity: isFavorite ? 1 : 0.2 }" @click="toggleFavorite">
 | 
				
			||||||
 | 
					        <template #icon>
 | 
				
			||||||
 | 
					          <n-icon :component="FavoriteFilled" />
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					      </n-button>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					    {{ isFavorite ? 'Remove from favorites' : 'Add to favorites' }}
 | 
				
			||||||
 | 
					  </n-tooltip>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import { FavoriteFilled } from '@vicons/material';
 | 
				
			||||||
 | 
					import { useToolStore } from '@/tools/tools.store';
 | 
				
			||||||
 | 
					import type { Tool } from '@/tools/tools.types';
 | 
				
			||||||
 | 
					import { computed, toRefs } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const toolStore = useToolStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps<{ tool: Tool }>();
 | 
				
			||||||
 | 
					const { tool } = toRefs(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isFavorite = computed(() => toolStore.isToolFavorite({ tool }));
 | 
				
			||||||
 | 
					const buttonType = computed(() => (isFavorite.value ? 'primary' : 'default'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function toggleFavorite(event: MouseEvent) {
 | 
				
			||||||
 | 
					  event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (toolStore.isToolFavorite({ tool })) {
 | 
				
			||||||
 | 
					    toolStore.removeToolFromFavorites({ tool });
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toolStore.addToolToFavorites({ tool });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped></style>
 | 
				
			||||||
@ -6,11 +6,11 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { ITool } from '@/tools/tool';
 | 
					import type { Tool } from '@/tools/tools.types';
 | 
				
			||||||
import { useThemeVars } from 'naive-ui';
 | 
					import { useThemeVars } from 'naive-ui';
 | 
				
			||||||
import { toRefs } from 'vue';
 | 
					import { toRefs } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{ tool: ITool }>();
 | 
					const props = defineProps<{ tool: Tool }>();
 | 
				
			||||||
const { tool } = toRefs(props);
 | 
					const { tool } = toRefs(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const theme = useThemeVars();
 | 
					const theme = useThemeVars();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { useFuzzySearch } from '@/composable/fuzzySearch';
 | 
					import { useFuzzySearch } from '@/composable/fuzzySearch';
 | 
				
			||||||
import { tools } from '@/tools';
 | 
					import { tools } from '@/tools';
 | 
				
			||||||
import type { ITool } from '@/tools/tool';
 | 
					import type { Tool } from '@/tools/tools.types';
 | 
				
			||||||
import { SearchRound } from '@vicons/material';
 | 
					import { SearchRound } from '@vicons/material';
 | 
				
			||||||
import { useMagicKeys, whenever } from '@vueuse/core';
 | 
					import { useMagicKeys, whenever } from '@vueuse/core';
 | 
				
			||||||
import { computed, h, ref } from 'vue';
 | 
					import { computed, h, ref } from 'vue';
 | 
				
			||||||
@ -17,7 +17,7 @@ const { searchResult } = useFuzzySearch({
 | 
				
			|||||||
  options: { keys: [{ name: 'name', weight: 2 }, 'description', 'keywords'] },
 | 
					  options: { keys: [{ name: 'name', weight: 2 }, 'description', 'keywords'] },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const toolToOption = (tool: ITool) => ({ label: tool.name, value: tool.path, tool });
 | 
					const toolToOption = (tool: Tool) => ({ label: tool.name, value: tool.path, tool });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const options = computed(() => {
 | 
					const options = computed(() => {
 | 
				
			||||||
  if (queryString.value === '') {
 | 
					  if (queryString.value === '') {
 | 
				
			||||||
@ -47,7 +47,7 @@ whenever(keys.ctrl_k, () => {
 | 
				
			|||||||
  focusTarget.value.focus();
 | 
					  focusTarget.value.focus();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderOption({ tool }: { tool: ITool }) {
 | 
					function renderOption({ tool }: { tool: Tool }) {
 | 
				
			||||||
  return h(SearchBarItem, { tool });
 | 
					  return h(SearchBarItem, { tool });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import type { ITool } from '@/tools/tool';
 | 
					import type { Tool } from '@/tools/tools.types';
 | 
				
			||||||
import { toRefs } from 'vue';
 | 
					import { toRefs } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{ tool: ITool }>();
 | 
					const props = defineProps<{ tool: Tool }>();
 | 
				
			||||||
const { tool } = toRefs(props);
 | 
					const { tool } = toRefs(props);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,17 +3,21 @@
 | 
				
			|||||||
    <n-card class="tool-card">
 | 
					    <n-card class="tool-card">
 | 
				
			||||||
      <n-space justify="space-between" align="center">
 | 
					      <n-space justify="space-between" align="center">
 | 
				
			||||||
        <n-icon class="icon" size="40" :component="tool.icon" />
 | 
					        <n-icon class="icon" size="40" :component="tool.icon" />
 | 
				
			||||||
        <n-tag
 | 
					        <n-space align="center">
 | 
				
			||||||
          v-if="tool.isNew"
 | 
					          <n-tag
 | 
				
			||||||
          size="small"
 | 
					            v-if="tool.isNew"
 | 
				
			||||||
          class="badge-new"
 | 
					            size="small"
 | 
				
			||||||
          round
 | 
					            class="badge-new"
 | 
				
			||||||
          type="success"
 | 
					            round
 | 
				
			||||||
          :bordered="false"
 | 
					            type="success"
 | 
				
			||||||
          :color="{ color: theme.primaryColor, textColor: theme.tagColor }"
 | 
					            :bordered="false"
 | 
				
			||||||
        >
 | 
					            :color="{ color: theme.primaryColor, textColor: theme.tagColor }"
 | 
				
			||||||
          New
 | 
					          >
 | 
				
			||||||
        </n-tag>
 | 
					            New
 | 
				
			||||||
 | 
					          </n-tag>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <favorite-button :tool="tool" />
 | 
				
			||||||
 | 
					        </n-space>
 | 
				
			||||||
      </n-space>
 | 
					      </n-space>
 | 
				
			||||||
      <n-h3 class="title">
 | 
					      <n-h3 class="title">
 | 
				
			||||||
        <n-ellipsis>{{ tool.name }}</n-ellipsis>
 | 
					        <n-ellipsis>{{ tool.name }}</n-ellipsis>
 | 
				
			||||||
@ -29,11 +33,12 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { ITool } from '@/tools/tool';
 | 
					import type { Tool } from '@/tools/tools.types';
 | 
				
			||||||
import { useThemeVars } from 'naive-ui';
 | 
					import { useThemeVars } from 'naive-ui';
 | 
				
			||||||
import { toRefs } from 'vue';
 | 
					import { toRefs } from 'vue';
 | 
				
			||||||
 | 
					import FavoriteButton from './FavoriteButton.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{ tool: ITool & { category: string } }>();
 | 
					const props = defineProps<{ tool: Tool & { category: string } }>();
 | 
				
			||||||
const { tool } = toRefs(props);
 | 
					const { tool } = toRefs(props);
 | 
				
			||||||
const theme = useThemeVars();
 | 
					const theme = useThemeVars();
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,12 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { toolsWithCategory } from '@/tools';
 | 
					import { useToolStore } from '@/tools/tools.store';
 | 
				
			||||||
import { Heart } from '@vicons/tabler';
 | 
					import { Heart } from '@vicons/tabler';
 | 
				
			||||||
import { useHead } from '@vueuse/head';
 | 
					import { useHead } from '@vueuse/head';
 | 
				
			||||||
import ColoredCard from '../components/ColoredCard.vue';
 | 
					import ColoredCard from '../components/ColoredCard.vue';
 | 
				
			||||||
import ToolCard from '../components/ToolCard.vue';
 | 
					import ToolCard from '../components/ToolCard.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const toolStore = useToolStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
useHead({ title: 'IT Tools - Handy online tools for developers' });
 | 
					useHead({ title: 'IT Tools - Handy online tools for developers' });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,8 +34,34 @@ useHead({ title: 'IT Tools - Handy online tools for developers' });
 | 
				
			|||||||
          <n-icon :component="Heart" />
 | 
					          <n-icon :component="Heart" />
 | 
				
			||||||
        </colored-card>
 | 
					        </colored-card>
 | 
				
			||||||
      </n-gi>
 | 
					      </n-gi>
 | 
				
			||||||
      <n-gi v-for="tool in toolsWithCategory" :key="tool.name">
 | 
					    </n-grid>
 | 
				
			||||||
        <tool-card :tool="tool" />
 | 
					
 | 
				
			||||||
 | 
					    <transition name="height">
 | 
				
			||||||
 | 
					      <div v-if="toolStore.favoriteTools.length > 0">
 | 
				
			||||||
 | 
					        <n-h3>Your favorite tools</n-h3>
 | 
				
			||||||
 | 
					        <n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
 | 
				
			||||||
 | 
					          <n-gi v-for="tool in toolStore.favoriteTools" :key="tool.name">
 | 
				
			||||||
 | 
					            <tool-card :tool="tool" />
 | 
				
			||||||
 | 
					          </n-gi>
 | 
				
			||||||
 | 
					        </n-grid>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </transition>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div v-if="toolStore.newTools.length > 0">
 | 
				
			||||||
 | 
					      <n-h3>Newest tools</n-h3>
 | 
				
			||||||
 | 
					      <n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
 | 
				
			||||||
 | 
					        <n-gi v-for="tool in toolStore.newTools" :key="tool.name">
 | 
				
			||||||
 | 
					          <tool-card :tool="tool" />
 | 
				
			||||||
 | 
					        </n-gi>
 | 
				
			||||||
 | 
					      </n-grid>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <n-h3>All the tools</n-h3>
 | 
				
			||||||
 | 
					    <n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
 | 
				
			||||||
 | 
					      <n-gi v-for="tool in toolStore.tools" :key="tool.name">
 | 
				
			||||||
 | 
					        <transition>
 | 
				
			||||||
 | 
					          <tool-card :tool="tool" />
 | 
				
			||||||
 | 
					        </transition>
 | 
				
			||||||
      </n-gi>
 | 
					      </n-gi>
 | 
				
			||||||
    </n-grid>
 | 
					    </n-grid>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
@ -43,4 +71,23 @@ useHead({ title: 'IT Tools - Handy online tools for developers' });
 | 
				
			|||||||
.home-page {
 | 
					.home-page {
 | 
				
			||||||
  padding-top: 50px;
 | 
					  padding-top: 50px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					::v-deep(.n-grid) {
 | 
				
			||||||
 | 
					  margin-bottom: 12px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.height-enter-active,
 | 
				
			||||||
 | 
					.height-leave-active {
 | 
				
			||||||
 | 
					  transition: all 0.5s ease-in-out;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  max-height: 500px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.height-enter-from,
 | 
				
			||||||
 | 
					.height-leave-to {
 | 
				
			||||||
 | 
					  max-height: 42px;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
import { LockOpen } from '@vicons/tabler';
 | 
					import { LockOpen } from '@vicons/tabler';
 | 
				
			||||||
import type { ToolCategory } from './tool';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { tool as chmodCalculator } from './chmod-calculator';
 | 
					import { tool as chmodCalculator } from './chmod-calculator';
 | 
				
			||||||
import { tool as mimeTypes } from './mime-types';
 | 
					import { tool as mimeTypes } from './mime-types';
 | 
				
			||||||
@ -36,16 +35,15 @@ import { tool as tokenGenerator } from './token-generator';
 | 
				
			|||||||
import { tool as urlEncoder } from './url-encoder';
 | 
					import { tool as urlEncoder } from './url-encoder';
 | 
				
			||||||
import { tool as urlParser } from './url-parser';
 | 
					import { tool as urlParser } from './url-parser';
 | 
				
			||||||
import { tool as uuidGenerator } from './uuid-generator';
 | 
					import { tool as uuidGenerator } from './uuid-generator';
 | 
				
			||||||
 | 
					import type { ToolCategory } from './tools.types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const toolsByCategory: ToolCategory[] = [
 | 
					export const toolsByCategory: ToolCategory[] = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Crypto',
 | 
					    name: 'Crypto',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [tokenGenerator, hashText, bcrypt, uuidGenerator, cypher, bip39, hmacGenerator],
 | 
					    components: [tokenGenerator, hashText, bcrypt, uuidGenerator, cypher, bip39, hmacGenerator],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Converter',
 | 
					    name: 'Converter',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [
 | 
					    components: [
 | 
				
			||||||
      dateTimeConverter,
 | 
					      dateTimeConverter,
 | 
				
			||||||
      baseConverter,
 | 
					      baseConverter,
 | 
				
			||||||
@ -58,7 +56,6 @@ export const toolsByCategory: ToolCategory[] = [
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Web',
 | 
					    name: 'Web',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [
 | 
					    components: [
 | 
				
			||||||
      urlEncoder,
 | 
					      urlEncoder,
 | 
				
			||||||
      htmlEntities,
 | 
					      htmlEntities,
 | 
				
			||||||
@ -72,27 +69,22 @@ export const toolsByCategory: ToolCategory[] = [
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Images',
 | 
					    name: 'Images',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [qrCodeGenerator, svgPlaceholderGenerator],
 | 
					    components: [qrCodeGenerator, svgPlaceholderGenerator],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Development',
 | 
					    name: 'Development',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [gitMemo, randomPortGenerator, crontabGenerator, jsonViewer, sqlPrettify, chmodCalculator],
 | 
					    components: [gitMemo, randomPortGenerator, crontabGenerator, jsonViewer, sqlPrettify, chmodCalculator],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Math',
 | 
					    name: 'Math',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [mathEvaluator, etaCalculator],
 | 
					    components: [mathEvaluator, etaCalculator],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Measurement',
 | 
					    name: 'Measurement',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [chronometer],
 | 
					    components: [chronometer],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'Text',
 | 
					    name: 'Text',
 | 
				
			||||||
    icon: LockOpen,
 | 
					 | 
				
			||||||
    components: [loremIpsumGenerator, textStatistics],
 | 
					    components: [loremIpsumGenerator, textStatistics],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +1,10 @@
 | 
				
			|||||||
import { config } from '@/config';
 | 
					import { config } from '@/config';
 | 
				
			||||||
import type { Component } from 'vue';
 | 
					import type { Tool } from './tools.types';
 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ITool {
 | 
					 | 
				
			||||||
  name: string;
 | 
					 | 
				
			||||||
  path: string;
 | 
					 | 
				
			||||||
  description: string;
 | 
					 | 
				
			||||||
  keywords: string[];
 | 
					 | 
				
			||||||
  component: () => Promise<Component>;
 | 
					 | 
				
			||||||
  icon: Component;
 | 
					 | 
				
			||||||
  redirectFrom?: string[];
 | 
					 | 
				
			||||||
  isNew: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface ToolCategory {
 | 
					 | 
				
			||||||
  name: string;
 | 
					 | 
				
			||||||
  icon: Component;
 | 
					 | 
				
			||||||
  components: ITool[];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
 | 
					type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function defineTool(
 | 
					export function defineTool(
 | 
				
			||||||
  tool: WithOptional<ITool, 'isNew'>,
 | 
					  tool: WithOptional<Tool, 'isNew'>,
 | 
				
			||||||
  { newTools }: { newTools: string[] } = { newTools: config.tools.newTools },
 | 
					  { newTools }: { newTools: string[] } = { newTools: config.tools.newTools },
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  const isNew = newTools.includes(tool.name);
 | 
					  const isNew = newTools.includes(tool.name);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								src/tools/tools.store.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/tools/tools.store.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					import { get, useStorage, type MaybeRef } from '@vueuse/core';
 | 
				
			||||||
 | 
					import { defineStore } from 'pinia';
 | 
				
			||||||
 | 
					import type { Ref } from 'vue';
 | 
				
			||||||
 | 
					import { toolsWithCategory } from './index';
 | 
				
			||||||
 | 
					import type { Tool, ToolWithCategory } from './tools.types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useToolStore = defineStore('tools', {
 | 
				
			||||||
 | 
					  state: () => ({
 | 
				
			||||||
 | 
					    favoriteToolsName: useStorage('favoriteToolsName', []) as Ref<string[]>,
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  getters: {
 | 
				
			||||||
 | 
					    favoriteTools(state) {
 | 
				
			||||||
 | 
					      return state.favoriteToolsName
 | 
				
			||||||
 | 
					        .map((favoriteName) => toolsWithCategory.find(({ name }) => name === favoriteName))
 | 
				
			||||||
 | 
					        .filter(Boolean) as ToolWithCategory[]; // cast because .filter(Boolean) does not remove undefined from type
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    notFavoriteTools(state): ToolWithCategory[] {
 | 
				
			||||||
 | 
					      return toolsWithCategory.filter((tool) => !state.favoriteToolsName.includes(tool.name));
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tools(): ToolWithCategory[] {
 | 
				
			||||||
 | 
					      return toolsWithCategory;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    newTools(): ToolWithCategory[] {
 | 
				
			||||||
 | 
					      return this.tools.filter(({ isNew }) => isNew);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  actions: {
 | 
				
			||||||
 | 
					    addToolToFavorites({ tool }: { tool: MaybeRef<Tool> }) {
 | 
				
			||||||
 | 
					      this.favoriteToolsName.push(get(tool).name);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    removeToolFromFavorites({ tool }: { tool: MaybeRef<Tool> }) {
 | 
				
			||||||
 | 
					      this.favoriteToolsName = this.favoriteToolsName.filter((name) => get(tool).name !== name);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    isToolFavorite({ tool }: { tool: MaybeRef<Tool> }) {
 | 
				
			||||||
 | 
					      return this.favoriteToolsName.includes(get(tool).name);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/tools/tools.types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/tools/tools.types.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import type { Component } from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Tool = {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  path: string;
 | 
				
			||||||
 | 
					  description: string;
 | 
				
			||||||
 | 
					  keywords: string[];
 | 
				
			||||||
 | 
					  component: () => Promise<Component>;
 | 
				
			||||||
 | 
					  icon: Component;
 | 
				
			||||||
 | 
					  redirectFrom?: string[];
 | 
				
			||||||
 | 
					  isNew: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ToolCategory = {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  components: Tool[];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ToolWithCategory = Tool & { category: string };
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user