first commit

This commit is contained in:
Illya Marchenko 2023-11-10 16:00:27 +02:00
commit 93953e52d7
Signed by: stuzer05
GPG Key ID: A6ABAAA9268F9F4F
25 changed files with 2385 additions and 0 deletions

14
.eslintrc.cjs Normal file

@ -0,0 +1,14 @@
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
"root": true,
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/eslint-config-prettier"
],
"env": {
"vue/setup-compiler-macros": true
}
}

27
.gitignore vendored Normal file

@ -0,0 +1,27 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
config.js
config.prod.js
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

29
Dockerfile Normal file

@ -0,0 +1,29 @@
FROM debian:bullseye-slim
ENV DEBIAN_FRONTEND="noninteractive"
RUN apt-get update && apt-get -y upgrade \
&& apt-get -y install \
curl nodejs npm \
&& npm install -g vue vite n \
&& n stable \
&& mkdir /app \
&& rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*
WORKDIR /app
COPY ./public public
COPY ./src src
COPY ./config.prod.js ./config.js
COPY ./index.html .
COPY ./package.json .
COPY ./package-lock.json .
COPY ./postcss.config.js .
COPY ./tailwind.config.js .
COPY ./vite.config.js .
RUN npm install && npm run build
FROM nginx
COPY --from=0 /app/dist /usr/share/nginx/html
COPY ./nginx/conf /etc/nginx/conf.d/

1
README.md Normal file

@ -0,0 +1 @@
# Random tools collection

6
config.js.example Normal file

@ -0,0 +1,6 @@
const config = {
APP_URL: 'http://localhost:3000',
// API_URL: 'http://localhost:8080/v1',
};
export { config }

13
index.html Normal file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Tools</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/app.js"></script>
</body>
</html>

23
nginx/conf/default.conf Normal file

@ -0,0 +1,23 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location ~ /map/\d+$ {
try_files $uri $uri/ /index.html;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

1768
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
package.json Normal file

@ -0,0 +1,33 @@
{
"name": "yourmap-tracker",
"version": "0.0.0",
"scripts": {
"build": "vite build",
"lint": "eslint .. --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"dev": "vite",
"preview": "vite preview --port 3000"
},
"dependencies": {
"autoprefixer": "^10",
"axios": "^0.27.2",
"mitt": "^3.0.0",
"pinia": "^2.0.14",
"postcss": "^8",
"tailwindcss": "^3",
"vue": "^3.2.36",
"vue-axios": "^3.4.1",
"vue-router": "^4.0.15"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.0",
"@vitejs/plugin-vue": "^2.3.3",
"@vue/eslint-config-prettier": "^7.0.0",
"autoprefixer": "^10.4.7",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^8.2.0",
"prettier": "^2.5.1",
"sass": "^1.52.3",
"vite": "^2.9.9",
"vue-cli-plugin-tailwind": "~3.0.0"
}
}

6
postcss.config.js Normal file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

71
src/App.vue Normal file

@ -0,0 +1,71 @@
<template>
<div
class="w-full flex flex-col sm:flex-row flex-grow"
:class="{ 'sidebar-toggle': sidebarToggle }">
<aside
class="hidden md:block h-screen overflow-y-auto no-scrollbar border-r">
<Sidebar/>
</aside>
<main role="main" class="w-full h-full flex-grow overflow-auto p-3">
<RouterView/>
</main>
<button class="sidebar-toggle-btn md:hidden" v-on:click="toggleSidebar">
<svg class="svg-icon" style="width: 30px; height: 30px;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M682.666667 682.666667h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666H170.666667v-170.666666z m512-256h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666H170.666667v-170.666666z m512-256h170.666666v170.666666h-170.666666V170.666667z m-256 0h170.666666v170.666666h-170.666666V170.666667zM170.666667 170.666667h170.666666v170.666666H170.666667V170.666667z" fill="#000000" /></svg>
</button>
</div>
</template>
<script>
import { RouterView } from 'vue-router'
import Sidebar from '@/components/Sidebar.vue';
export default {
components: {
Sidebar,
RouterView,
},
data() {
return {
sidebarToggle: false,
};
},
methods: {
toggleSidebar() {
this.sidebarToggle = !this.sidebarToggle;
this.emitter.emit('sidebar.toggle');
},
},
}
</script>
<style lang="scss">
aside {
width: 480px;
}
@media (max-width: 768px) {
.sidebar-toggle {
aside {
display: block;
width: 100%;
max-width: initial;
}
main {
display: none;
}
&-btn {
right: 10px;
bottom: 10px;
position: fixed;
z-index: 1000;
background-color: white;
border-radius: 5px;
padding: 2px;
}
}
}
</style>

23
src/app.js Normal file

@ -0,0 +1,23 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import axios from 'axios'
import VueAxios from 'vue-axios'
import mitt from 'mitt';
import './assets/app.scss'
import App from './App.vue'
import router from './router'
axios.defaults.withCredentials = true;
const app = createApp(App)
app.config.globalProperties.emitter = mitt();
app.use(createPinia())
app.use(VueAxios, axios)
app.use(router)
app.mount('#app')

27
src/assets/app.scss Normal file

@ -0,0 +1,27 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
/**
* Components
*/
input[type=text], input[type=number], textarea {
@apply appearance-none border leading-tight focus:outline-none;
}
/**
* Tools
*/
.tool-title {
@apply font-bold;
font-size: 1.2rem;
}
.input-group {
@apply flex flex-col mb-3;
}
.input-group > label {
font-style: italic;
}

@ -0,0 +1,31 @@
<template>
<header class="flex">
<span class="my-auto ml-2 font-bold">Tools</span>
</header>
<hr class="mt-2 mb-2">
<nav class="flex flex-col ml-2">
<router-link v-for="[key, value] of Object.entries(menuRoutes)" :to="{name:key}" tag="button">{{ value }}</router-link>
</nav>
</template>
<script>
export default {
name: "Sidebar",
components: {},
data() {
return {
menuRoutes: {
'home': 'Home',
'sql_split_in': 'SQL split IN',
'str_to_upper': 'Str to upper',
'str_to_lower': 'Str to lower',
},
};
},
mounted() {
}
}
</script>

40
src/router/index.js Normal file

@ -0,0 +1,40 @@
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
/**
* General
*/
{
path: '/',
name: 'home',
component: () => import('../views/HomeView.vue'),
},
/**
* String manipulation
*/
{
path: '/str_to_upper',
name: 'str_to_upper',
component: () => import('../views/StrToUpper.vue'),
},
{
path: '/str_to_lower',
name: 'str_to_lower',
component: () => import('../views/StrToLower.vue'),
},
/**
* SQL manipulation
*/
{
path: '/sql_split_in',
name: 'sql_split_in',
component: () => import('../views/SQLSplitInView.vue'),
},
],
})
export default router

0
src/stores/.gitkeep Normal file

7
src/utils/unproxy.js Normal file

@ -0,0 +1,7 @@
function unproxy(obj) {
return JSON.parse(JSON.stringify(obj));
}
export {
unproxy,
}

@ -0,0 +1,36 @@
import { ref, customRef } from 'vue'
const debounce = (fn, delay = 0, immediate = false) => {
let timeout
return (...args) => {
if (immediate && !timeout) fn(...args)
clearTimeout(timeout)
timeout = setTimeout(() => {
fn(...args)
}, delay)
}
}
const useDebouncedRef = (initialValue, delay, immediate) => {
const state = ref(initialValue)
const debouncedRef = customRef((track, trigger) => ({
get() {
track()
return state.value
},
set: debounce(
value => {
state.value = value
trigger()
},
delay,
immediate
),
}))
return debouncedRef
}
export {
useDebouncedRef,
}

7
src/views/HomeView.vue Normal file

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

@ -0,0 +1,103 @@
<template>
<h2 class="tool-title">SQL split IN</h2>
<hr class="mt-5 mb-5">
<div class="input-group">
<label for="field_name">Field name</label>
<input id="field_name" v-model="toolData.fieldName" placeholder="D.ID" type="text">
</div>
<div class="input-group">
<label for="data">Data</label>
<textarea id="data" v-model="toolData.data" style="height: 150px"></textarea>
</div>
<div class="input-group">
<label for="data_delimiter">Delimiter</label>
<input id="data_delimiter" v-model="toolData.dataDelimiter" placeholder="," type="text">
</div>
<div class="input-group">
<label>Wrap in quotes</label>
<div>
<input id="wrap_in_quotes_no" value="" name="wrap_in_quotes" v-model="toolData.wrapInQuotes" type="radio"> <label for="wrap_in_quotes_no">No</label><br>
<input id="wrap_in_quotes_single" value="single" name="wrap_in_quotes" v-model="toolData.wrapInQuotes" type="radio"> <label for="wrap_in_quotes_single">Single</label><br>
<input id="wrap_in_quotes_double" value="double" name="wrap_in_quotes" v-model="toolData.wrapInQuotes" type="radio"> <label for="wrap_in_quotes_double">Double</label>
</div>
</div>
<div class="input-group">
<label for="chunk_by">Chunk by</label>
<input id="chunk_by" v-model="toolData.chunkBy" placeholder="900" type="number">
</div>
<hr class="mt-5 mb-5">
<div class="input-group">
<label for="result">Result</label>
<textarea id="result" v-model="toolResult" style="height: 150px"></textarea>
</div>
</template>
<script>
function arrayChunk(arr, chunkSize) {
const res = [];
while (arr.length > 0) {
const chunk = arr.splice(0, chunkSize);
res.push(chunk);
}
return res;
}
export default {
data() {
return {
toolData: {
fieldName: '',
data: '',
dataDelimiter: '',
wrapInQuotes: '',
chunkBy: 900,
},
};
},
computed: {
toolResult() {
let data = this.toolData.data
.split(this.toolData.dataDelimiter ? this.toolData.dataDelimiter : '\n');
for (let i = 0; i < data.length; i++) {
let value = data[i];
switch (this.toolData.wrapInQuotes) {
case 'single':
value = "'" + value.replace("'", "\\'") + "'";
break;
case 'double':
value = '"' + value.replace('"', '\\"') + '"';
break;
}
data[i] = value;
}
data = arrayChunk(data, this.toolData.chunkBy);
for (let i = 0; i < data.length; i++) {
let chunk = data[i];
data[i] = '(' + chunk.join(',') + ')';
}
return '(' + this.toolData.fieldName + ' IN ' + data.join(' OR ' + this.toolData.fieldName + ' IN ') + ')';
},
},
}
</script>
<style lang="scss">
</style>

37
src/views/StrToLower.vue Normal file

@ -0,0 +1,37 @@
<template>
<h2 class="tool-title">Str to lower</h2>
<hr class="mt-5 mb-5">
<div class="input-group">
<label for="data">Data</label>
<textarea id="data" v-model="toolData.data" style="height: 150px"></textarea>
</div>
<hr class="mt-5 mb-5">
<div class="input-group">
<label for="result">Result</label>
<textarea id="result" v-model="toolResult" style="height: 150px"></textarea>
</div>
</template>
<script>
export default {
data() {
return {
toolData: {
data: '',
},
};
},
computed: {
toolResult() {
return this.toolData.data.toLocaleLowerCase();
},
},
}
</script>
<style lang="scss">
</style>

37
src/views/StrToUpper.vue Normal file

@ -0,0 +1,37 @@
<template>
<h2 class="tool-title">Str to upper</h2>
<hr class="mt-5 mb-5">
<div class="input-group">
<label for="data">Data</label>
<textarea id="data" v-model="toolData.data" style="height: 150px"></textarea>
</div>
<hr class="mt-5 mb-5">
<div class="input-group">
<label for="result">Result</label>
<textarea id="result" v-model="toolResult" style="height: 150px"></textarea>
</div>
</template>
<script>
export default {
data() {
return {
toolData: {
data: '',
},
};
},
computed: {
toolResult() {
return this.toolData.data.toLocaleUpperCase();
},
},
}
</script>
<style lang="scss">
</style>

17
tailwind.config.js Normal file

@ -0,0 +1,17 @@
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
// "./node_modules/flowbite/**/*.js",
],
theme: {
extend: {
zIndex: {
'500': '500',
}
},
},
plugins: [
// require('flowbite/plugin')
],
}

29
vite.config.js Normal file

@ -0,0 +1,29 @@
import {fileURLToPath, URL} from 'url'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
function pathTo(path) {
return fileURLToPath(new URL(path, import.meta.url));
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': pathTo('./src', import.meta.url),
'@node_modules': pathTo('./node_modules'),
}
},
server: {
host: true,
port: 3000,
cors: false,
},
preview: {
host: true,
port: 3000,
cors: false,
}
})