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

View File

@ -1,12 +1,13 @@
<template>
<header class="flex">
<span class="my-auto ml-2 font-bold">Tools</span>
<header class="flex justify-between mx-2 mt-1">
<span class="font-bold">Tools</span>
<router-link :to="{name:'home'}" class="ml-4" tag="button">Home</router-link>
</header>
<hr class="mt-2 mb-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>
<router-link v-for="[key, value] of Object.entries(routes)" :to="{name:key}" class="ml-4" tag="button">{{ value }}</router-link>
</div>
@ -15,7 +16,7 @@
<div class="flex flex-col mb-2">
<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://pdf.tools.stuzer.link/" class="ml-4" tag="button">PDF tools</a>
</div>
@ -24,6 +25,8 @@
<script>
import { useToolsStore } from "@/stores/toolsStore";
export default {
name: "Sidebar",
components: {},
@ -33,54 +36,7 @@ export default {
};
},
mounted() {
this.menuRoutes = {
'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',
},
};
this.menuRoutes = useToolsStore().tools;
}
}
</script>

View File

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

77
src/stores/toolsStore.js Normal file
View 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
View 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>

View File

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

View File

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