Add home page tiles

This commit is contained in:
Illya Marchenko 2024-10-10 12:36:15 +03:00
parent faabc828d6
commit 1c5b0ba2ec
Signed by: stuzer05
GPG Key ID: A6ABAAA9268F9F4F
9 changed files with 324 additions and 248 deletions

@ -1,12 +1,13 @@
<template> <template>
<header class="flex"> <header class="flex justify-between mx-2 mt-1">
<span class="my-auto ml-2 font-bold">Tools</span> <span class="font-bold">Tools</span>
<router-link :to="{name:'home'}" class="ml-4" tag="button">Home</router-link>
</header> </header>
<hr class="mt-2 mb-2"> <hr class="mt-2 mb-2">
<nav class="flex flex-col ml-2"> <nav class="flex flex-col ml-2">
<div v-for="[title, routes] of Object.entries(menuRoutes)" class="flex flex-col mb-2"> <div v-for="[title, routes] of Object.entries(menuRoutes).sort((a, b) => a[0].localeCompare(b[0]))" class="flex flex-col mb-2">
<p><i>{{ title }}</i></p> <p><i>{{ title }}</i></p>
<router-link v-for="[key, value] of Object.entries(routes)" :to="{name:key}" class="ml-4" tag="button">{{ value }}</router-link> <router-link v-for="[key, value] of Object.entries(routes)" :to="{name:key}" class="ml-4" tag="button">{{ value }}</router-link>
</div> </div>
@ -15,7 +16,7 @@
<div class="flex flex-col mb-2"> <div class="flex flex-col mb-2">
<p><i>Other</i></p> <p><i>Other</i></p>
<a href="https://gist.stuzer.link/" class="ml-4" tag="button">Gist</a> <a href="https://gist.stuzer.link/stuzer05/liked" class="ml-4" tag="button">Gist</a>
<a href="https://cyberchef.tools.stuzer.link/" class="ml-4" tag="button">Cyberchef</a> <a href="https://cyberchef.tools.stuzer.link/" class="ml-4" tag="button">Cyberchef</a>
<a href="https://pdf.tools.stuzer.link/" class="ml-4" tag="button">PDF tools</a> <a href="https://pdf.tools.stuzer.link/" class="ml-4" tag="button">PDF tools</a>
</div> </div>
@ -24,6 +25,8 @@
<script> <script>
import { useToolsStore } from "@/stores/toolsStore";
export default { export default {
name: "Sidebar", name: "Sidebar",
components: {}, components: {},
@ -33,54 +36,7 @@ export default {
}; };
}, },
mounted() { mounted() {
this.menuRoutes = { this.menuRoutes = useToolsStore().tools;
'General': {
'home': 'Home',
'explain_crontab': 'Explain crontab',
'table_to_markdown_table': 'Table to Markdown table',
'table_to_mediawiki_table': 'Table to Mediawiki table',
'dummy_image': 'Dummy image',
'humans_txt': 'humans.txt generator',
'file_base64_encode_decode': 'File base64 encode/decode',
'qr_code': 'QR code',
'unix_timestamp': 'Unix timestamp',
'sed_generator': 'Sed generator',
'htaccess_generator': '.htaccess generator',
},
'Strings': {
'fix_ru_en_keyboard': 'Fix ru-en keyboard',
'str_length': 'Str length',
'str_sort_lines': 'Str sort lines',
'str_to_lower_upper': 'Str to lower/upper',
'str_remove_duplicate_lines': 'Str remove duplicate lines',
'str_pad': 'Str pad',
'str_numeronym': 'Str numeronym (i18n)',
'str_to_nato_alphabet': 'Str to NATO alphabet',
'url_encode_decode': 'URL encode/decode',
'url_query_viewer': 'URL query viewer',
'iban_generator': 'IBAN generator',
},
'PHP': {
'str_to_php_array': 'Str to PHP array',
'php_array_to_json': 'PHP array to Json',
'php_serialize': 'PHP serialize',
},
'Docker': {
'docker_rename_volume': 'Rename volume',
},
'GO': {
'go_json_to_struct': 'JSON to Go struct',
// 'sql_tables_to_struct': 'SQL tables Go struct',
},
'JSON': {
'json_minifier': 'JSON minifier',
'json_formatter': 'JSON formatter',
},
'SQL': {
'sql_formatter': 'SQL formatter',
'sql_split_in': 'SQL split IN',
},
};
} }
} }
</script> </script>

@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@ -7,190 +7,190 @@ const router = createRouter({
* General * General
*/ */
{ {
path: '/', path: "/",
name: 'home', name: "home",
component: () => import('../views/general/HomeView.vue'), component: () => import("../views/HomeView.vue")
}, },
{ {
path: '/explain_crontab', path: "/explain_crontab",
name: 'explain_crontab', name: "explain_crontab",
component: () => import('../views/general/ExplainCrontab.vue'), component: () => import("../views/general/ExplainCrontab.vue")
}, },
{ {
path: '/table_to_markdown_table', path: "/table_to_markdown_table",
name: 'table_to_markdown_table', name: "table_to_markdown_table",
component: () => import('../views/general/TableToMarkdownTable.vue'), component: () => import("../views/general/TableToMarkdownTable.vue")
}, },
{ {
path: '/table_to_mediawiki_table', path: "/table_to_mediawiki_table",
name: 'table_to_mediawiki_table', name: "table_to_mediawiki_table",
component: () => import('../views/general/TableToMediawikiTable.vue'), component: () => import("../views/general/TableToMediawikiTable.vue")
}, },
{ {
path: '/dummy_image', path: "/dummy_image",
name: 'dummy_image', name: "dummy_image",
component: () => import('../views/general/DummyImage.vue'), component: () => import("../views/generators/DummyImage.vue")
}, },
{ {
path: '/humans_txt', path: "/humans_txt",
name: 'humans_txt', name: "humans_txt",
component: () => import('../views/general/HumansTxt.vue'), component: () => import("../views/general/HumansTxt.vue")
}, },
{ {
path: '/qr_code', path: "/qr_code",
name: 'qr_code', name: "qr_code",
component: () => import('../views/general/QRCode.vue'), component: () => import("../views/generators/QRCode.vue")
}, },
{ {
path: '/unix_timestamp', path: "/unix_timestamp",
name: 'unix_timestamp', name: "unix_timestamp",
component: () => import('../views/general/UnixTimestamp.vue'), component: () => import("../views/general/UnixTimestamp.vue")
}, },
{ {
path: '/file_base64_encode_decode', path: "/file_base64_encode_decode",
name: 'file_base64_encode_decode', name: "file_base64_encode_decode",
component: () => import('../views/general/FileBase64EncodeDecode.vue'), component: () => import("../views/general/FileBase64EncodeDecode.vue")
}, },
{ {
path: '/sed_generator', path: "/sed_generator",
name: 'sed_generator', name: "sed_generator",
component: () => import('../views/general/SedGenerator.vue'), component: () => import("../views/general/SedGenerator.vue")
}, },
{ {
path: '/htaccess_generator', path: "/htaccess_generator",
name: 'htaccess_generator', name: "htaccess_generator",
component: () => import('../views/general/HtaccessGenerator.vue'), component: () => import("../views/general/HtaccessGenerator.vue")
}, },
/** /**
* String manipulation * String manipulation
*/ */
{ {
path: '/str_length', path: "/str_length",
name: 'str_length', name: "str_length",
component: () => import('../views/strings/Length.vue'), component: () => import("../views/strings/Length.vue")
}, },
{ {
path: '/str_sort_lines', path: "/str_sort_lines",
name: 'str_sort_lines', name: "str_sort_lines",
component: () => import('../views/strings/SortLines.vue'), component: () => import("../views/strings/SortLines.vue")
}, },
{ {
path: '/str_to_lower_upper', path: "/str_to_lower_upper",
name: 'str_to_lower_upper', name: "str_to_lower_upper",
component: () => import('../views/strings/ToLowerUppper.vue'), component: () => import("../views/strings/ToLowerUppper.vue")
}, },
{ {
path: '/str_remove_duplicate_lines', path: "/str_remove_duplicate_lines",
name: 'str_remove_duplicate_lines', name: "str_remove_duplicate_lines",
component: () => import('../views/strings/RemoveDuplicateLines.vue'), component: () => import("../views/strings/RemoveDuplicateLines.vue")
}, },
{ {
path: '/str_pad', path: "/str_pad",
name: 'str_pad', name: "str_pad",
component: () => import('../views/strings/Pad.vue'), component: () => import("../views/strings/Pad.vue")
}, },
{ {
path: '/str_numeronym', path: "/str_numeronym",
name: 'str_numeronym', name: "str_numeronym",
component: () => import('../views/strings/Numeronym.vue'), component: () => import("../views/strings/Numeronym.vue")
}, },
{ {
path: '/str_to_nato_alphabet', path: "/str_to_nato_alphabet",
name: 'str_to_nato_alphabet', name: "str_to_nato_alphabet",
component: () => import('../views/strings/NATOAlphabet.vue'), component: () => import("../views/strings/NATOAlphabet.vue")
}, },
{ {
path: '/url_encode_decode', path: "/url_encode_decode",
name: 'url_encode_decode', name: "url_encode_decode",
component: () => import('../views/strings/UrlEncodeDecode.vue'), component: () => import("../views/strings/UrlEncodeDecode.vue")
}, },
{ {
path: '/url_query_viewer', path: "/url_query_viewer",
name: 'url_query_viewer', name: "url_query_viewer",
component: () => import('../views/strings/UrlQueryViewer.vue'), component: () => import("../views/strings/UrlQueryViewer.vue")
}, },
{ {
path: '/fix_ru_en_keyboard', path: "/fix_ru_en_keyboard",
name: 'fix_ru_en_keyboard', name: "fix_ru_en_keyboard",
component: () => import('../views/strings/FixRuEnKeyboard.vue'), component: () => import("../views/strings/FixRuEnKeyboard.vue")
}, },
{ {
path: '/iban_generator', path: "/iban_generator",
name: 'iban_generator', name: "iban_generator",
component: () => import('../views/strings/IbanGenerator.vue'), component: () => import("../views/generators/IbanGenerator.vue")
}, },
/** /**
* PHP * PHP
*/ */
{ {
path: '/str_to_php_array', path: "/str_to_php_array",
name: 'str_to_php_array', name: "str_to_php_array",
component: () => import('../views/php/StrToPHPArray.vue'), component: () => import("../views/php/StrToPHPArray.vue")
}, },
{ {
path: '/php_array_to_json', path: "/php_array_to_json",
name: 'php_array_to_json', name: "php_array_to_json",
component: () => import('../views/php/PHPArrayToJson.vue'), component: () => import("../views/php/PHPArrayToJson.vue")
}, },
{ {
path: '/php_serialize', path: "/php_serialize",
name: 'php_serialize', name: "php_serialize",
component: () => import('../views/php/Serialize.vue'), component: () => import("../views/php/Serialize.vue")
}, },
/** /**
* JSON manipulation * JSON manipulation
*/ */
{ {
path: '/json_formatter', path: "/json_formatter",
name: 'json_formatter', name: "json_formatter",
component: () => import('../views/json/JSONFormatter.vue'), component: () => import("../views/json/JSONFormatter.vue")
}, },
{ {
path: '/json_minifier', path: "/json_minifier",
name: 'json_minifier', name: "json_minifier",
component: () => import('../views/json/JSONMinifier.vue'), component: () => import("../views/json/JSONMinifier.vue")
}, },
/** /**
* Golang * Golang
*/ */
{ {
path: '/go_json_to_struct', path: "/go_json_to_struct",
name: 'go_json_to_struct', name: "go_json_to_struct",
component: () => import('../views/go/JSONToStruct.vue'), component: () => import("../views/go/JSONToStruct.vue")
}, },
{ {
path: '/sql_tables_to_struct', path: "/sql_tables_to_struct",
name: 'sql_tables_to_struct', name: "sql_tables_to_struct",
component: () => import('../views/go/SQLTablesToStruct.vue'), component: () => import("../views/go/SQLTablesToStruct.vue")
}, },
/** /**
* Docker * Docker
*/ */
{ {
path: '/docker_rename_volume', path: "/docker_rename_volume",
name: 'docker_rename_volume', name: "docker_rename_volume",
component: () => import('../views/docker/RenameVolume.vue'), component: () => import("../views/docker/RenameVolume.vue")
}, },
/** /**
* SQL manipulation * SQL manipulation
*/ */
{ {
path: '/sql_split_in', path: "/sql_split_in",
name: 'sql_split_in', name: "sql_split_in",
component: () => import('../views/sql/SplitInView.vue'), component: () => import("../views/sql/SplitInView.vue")
}, },
{ {
path: '/sql_formatter', path: "/sql_formatter",
name: 'sql_formatter', name: "sql_formatter",
component: () => import('../views/sql/Formatter.vue'), component: () => import("../views/sql/Formatter.vue")
}, }
], ]
}) });
export default router export default router;

77
src/stores/toolsStore.js Normal file

@ -0,0 +1,77 @@
import { defineStore } from 'pinia'
function sortMenuRoutes(routes) {
const sortedRoutes = {};
const sortedKeys = Object.keys(routes).sort();
for (const key of sortedKeys) {
const innerRoutes = routes[key];
const sortedInnerKeys = Object.keys(innerRoutes).sort();
sortedRoutes[key] = {};
for (const innerKey of sortedInnerKeys) {
sortedRoutes[key][innerKey] = innerRoutes[innerKey];
}
}
return sortedRoutes;
}
export const useToolsStore = defineStore('tools', {
state: () => ({
tools: sortMenuRoutes({
'-': {
'table_to_markdown_table': 'Table to Markdown table',
'table_to_mediawiki_table': 'Table to Mediawiki table',
'humans_txt': 'humans.txt generator',
},
'Docker': {
'docker_rename_volume': 'Rename volume',
},
'GO': {
'go_json_to_struct': 'JSON to Go struct',
// 'sql_tables_to_struct': 'SQL tables Go struct',
},
'JSON': {
'json_minifier': 'JSON minifier',
'json_formatter': 'JSON formatter',
},
'PHP': {
'str_to_php_array': 'Str to PHP array',
'php_array_to_json': 'PHP array to Json',
'php_serialize': 'PHP serialize',
},
'SQL': {
'sql_formatter': 'SQL formatter',
'sql_split_in': 'SQL split IN',
},
'Strings': {
'fix_ru_en_keyboard': 'Fix ru-en keyboard',
'str_length': 'Str length',
'str_sort_lines': 'Str sort lines',
'str_to_lower_upper': 'Str to lower/upper',
'str_remove_duplicate_lines': 'Str remove duplicate lines',
'str_pad': 'Str pad',
'str_numeronym': 'Str numeronym (i18n)',
'str_to_nato_alphabet': 'Str to NATO alphabet',
'url_encode_decode': 'URL encode/decode',
'url_query_viewer': 'URL query viewer',
},
'Unix': {
'explain_crontab': 'Explain crontab',
'file_base64_encode_decode': 'File base64 encode/decode',
'unix_timestamp': 'Unix timestamp',
'sed_generator': 'Sed generator',
'htaccess_generator': '.htaccess generator',
},
'Generators': {
'qr_code': 'QR code',
'iban_generator': 'IBAN generator',
'dummy_image': 'Dummy image',
},
}),
actions: {
// You can add actions here if needed, e.g., to update the tools
},
}),
})

50
src/views/HomeView.vue Normal file

@ -0,0 +1,50 @@
<template>
<div class="container mx-auto p-4">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-5">
<div v-for="(category, categoryName) in tools" :key="categoryName" class="bg-white rounded-lg shadow-md p-4">
<h2 class="text-xl font-semibold mb-3">{{ categoryName }}</h2>
<div class="max-h-80 overflow-y-auto">
<ul class="space-y-2">
<li
v-for="(value, key) in category"
:key="key"
@click="navigateTo(key)"
class="bg-gray-100 rounded p-2 hover:bg-gray-200 transition-colors duration-200 cursor-pointer"
>
{{ value }}
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import { useToolsStore } from "@/stores/toolsStore";
import { useRouter } from "vue-router";
export default {
data() {
return {
tools: {},
}
},
setup() {
const router = useRouter();
const navigateTo = (routeName) => {
router.push({ name: routeName });
};
return { navigateTo };
},
mounted() {
this.tools = useToolsStore().tools;
}
}
</script>
<style lang="scss">
</style>

@ -1,7 +0,0 @@
<template>
/home
</template>
<style lang="scss">
</style>

@ -28,7 +28,7 @@
<div> <div>
<input id="inputTimestamp" type="number" v-model="toolData.inputTimestamp" :placeholder="currentTimestamp"> <input id="inputTimestamp" type="number" v-model="toolData.inputTimestamp" :placeholder="currentTimestamp">
<button @click="convertFromUnix">Convert</button> <button @click="convertFromUnix">Convert </button>
</div> </div>
</div> </div>
@ -57,7 +57,7 @@
<td><input type="number" v-model="toolData.inputMinute"></td> <td><input type="number" v-model="toolData.inputMinute"></td>
<td><input type="number" v-model="toolData.inputSecond"></td> <td><input type="number" v-model="toolData.inputSecond"></td>
<td> <td>
<button @click="convertToUnix">Convert </button> <button @click="convertToUnix">Convert </button>
</td> </td>
</tr> </tr>
</tbody> </tbody>