mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-08 07:11:03 -04:00
Merge branch 'main' into main
This commit is contained in:
commit
542f358b91
11 changed files with 87 additions and 44 deletions
1
components.d.ts
vendored
1
components.d.ts
vendored
|
@ -33,6 +33,7 @@ declare module '@vue/runtime-core' {
|
||||||
CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default']
|
CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default']
|
||||||
'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default']
|
'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default']
|
||||||
CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default']
|
CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default']
|
||||||
|
CKeyValueListItem: typeof import('./src/ui/c-key-value-list/c-key-value-list-item.vue')['default']
|
||||||
CLabel: typeof import('./src/ui/c-label/c-label.vue')['default']
|
CLabel: typeof import('./src/ui/c-label/c-label.vue')['default']
|
||||||
CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
|
CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
|
||||||
'CLink.demo': typeof import('./src/ui/c-link/c-link.demo.vue')['default']
|
'CLink.demo': typeof import('./src/ui/c-link/c-link.demo.vue')['default']
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
"plausible-tracker": "^0.3.8",
|
"plausible-tracker": "^0.3.8",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.1",
|
||||||
"randombytes": "^2.1.0",
|
"randombytes": "^2.1.0",
|
||||||
"sql-formatter": "^12.0.0",
|
"sql-formatter": "^13.0.0",
|
||||||
"ua-parser-js": "^1.0.35",
|
"ua-parser-js": "^1.0.35",
|
||||||
"unicode-emoji-json": "^0.4.0",
|
"unicode-emoji-json": "^0.4.0",
|
||||||
"unplugin-auto-import": "^0.16.4",
|
"unplugin-auto-import": "^0.16.4",
|
||||||
|
|
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
|
@ -129,8 +129,8 @@ dependencies:
|
||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
sql-formatter:
|
sql-formatter:
|
||||||
specifier: ^12.0.0
|
specifier: ^13.0.0
|
||||||
version: 12.0.0
|
version: 13.0.0
|
||||||
ua-parser-js:
|
ua-parser-js:
|
||||||
specifier: ^1.0.35
|
specifier: ^1.0.35
|
||||||
version: 1.0.35
|
version: 1.0.35
|
||||||
|
@ -5565,6 +5565,11 @@ packages:
|
||||||
resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
|
resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/get-stdin@8.0.0:
|
||||||
|
resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/get-stream@6.0.1:
|
/get-stream@6.0.1:
|
||||||
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -6713,6 +6718,7 @@ packages:
|
||||||
|
|
||||||
/nearley@2.20.1:
|
/nearley@2.20.1:
|
||||||
resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==}
|
resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==}
|
||||||
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
commander: 2.20.3
|
commander: 2.20.3
|
||||||
moo: 0.5.2
|
moo: 0.5.2
|
||||||
|
@ -7820,10 +7826,12 @@ packages:
|
||||||
resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
|
resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/sql-formatter@12.0.0:
|
/sql-formatter@13.0.0:
|
||||||
resolution: {integrity: sha512-LR2m7BEvkyNAPzmcSCZ2b4Qzm5ySiiXS9Juc73VguTqCWIbYv7ZFV4LaDM7jNNZqHPfrqFssO7WWpITsAuLOuQ==}
|
resolution: {integrity: sha512-V21cVvge4rhn9Fa7K/fTKcmPM+x1yee6Vhq8ZwgaWh3VPBqApgsaoFB5kLAhiqRo5AmSaRyLU7LIdgnNwH01/w==}
|
||||||
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
get-stdin: 8.0.0
|
||||||
nearley: 2.20.1
|
nearley: 2.20.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ const { t } = useI18n();
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
<div v-if="toolStore.newTools.length > 0">
|
<div v-if="toolStore.newTools.length > 0">
|
||||||
<n-h3>{{ t('home.categories.newestTools', 'Newest tools') }}</n-h3>
|
<n-h3>{{ t('home.categories.newestTools') }}</n-h3>
|
||||||
<n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
|
<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">
|
<n-gi v-for="tool in toolStore.newTools" :key="tool.name">
|
||||||
<ToolCard :tool="tool" />
|
<ToolCard :tool="tool" />
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
import type { App } from 'vue';
|
import type { Plugin } from 'vue';
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
import messages from '@intlify/unplugin-vue-i18n/messages';
|
import baseMessages from '@intlify/unplugin-vue-i18n/messages';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { parse as parseYaml } from 'yaml';
|
||||||
|
|
||||||
|
const i18nFiles = import.meta.glob('../tools/*/locales/**.yml', { as: 'raw' });
|
||||||
|
|
||||||
|
const messagesByTools = await Promise.all(_.map(i18nFiles, async (fileDescriptor, path) => {
|
||||||
|
const [, locale] = path.match(/\.\/tools\/.*?\/locales\/(.*)\.ya?ml$/i) ?? [];
|
||||||
|
const content = parseYaml(await fileDescriptor());
|
||||||
|
|
||||||
|
return { [locale]: content };
|
||||||
|
}));
|
||||||
|
|
||||||
|
const messages = _.merge(
|
||||||
|
baseMessages,
|
||||||
|
_.merge({}, ...messagesByTools),
|
||||||
|
);
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
|
@ -8,8 +24,8 @@ const i18n = createI18n({
|
||||||
messages,
|
messages,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const i18nPlugin = {
|
export const i18nPlugin: Plugin = {
|
||||||
install: (app: App) => {
|
install: (app) => {
|
||||||
app.use(i18n);
|
app.use(i18n);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { type Page, expect, test } from '@playwright/test';
|
import { type Page, expect, test } from '@playwright/test';
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
async function extractIbanInfo({ page }: { page: Page }) {
|
async function extractIbanInfo({ page }: { page: Page }) {
|
||||||
const tdHandles = await page.locator('table tr td').elementHandles();
|
const itemsLines = await page
|
||||||
const tdTextContents = await Promise.all(tdHandles.map(el => el.textContent()));
|
.locator('.c-key-value-list__item').all();
|
||||||
|
|
||||||
return _.chain(tdTextContents)
|
return await Promise.all(
|
||||||
.map(tdTextContent => tdTextContent?.trim().replace(' Copy to clipboard', ''))
|
itemsLines.map(async item => [
|
||||||
.chunk(2)
|
(await item.locator('.c-key-value-list__key').textContent() ?? '').trim(),
|
||||||
.value();
|
(await item.locator('.c-key-value-list__value').textContent() ?? '').trim(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Tool - Iban validator and parser', () => {
|
test.describe('Tool - Iban validator and parser', () => {
|
||||||
|
@ -41,7 +42,7 @@ test.describe('Tool - Iban validator and parser', () => {
|
||||||
|
|
||||||
expect(ibanInfo).toEqual([
|
expect(ibanInfo).toEqual([
|
||||||
['Is IBAN valid ?', 'No'],
|
['Is IBAN valid ?', 'No'],
|
||||||
['IBAN errors', 'Wrong account bank branch checksumWrong IBAN checksum Copy to clipboard'],
|
['IBAN errors', 'Wrong account bank branch checksum Wrong IBAN checksum'],
|
||||||
['Is IBAN a QR-IBAN ?', 'No'],
|
['Is IBAN a QR-IBAN ?', 'No'],
|
||||||
['Country code', 'N/A'],
|
['Country code', 'N/A'],
|
||||||
['BBAN', 'N/A'],
|
['BBAN', 'N/A'],
|
||||||
|
|
|
@ -60,7 +60,7 @@ const ibanExamples = [
|
||||||
<div>
|
<div>
|
||||||
<c-input-text v-model:value="rawIban" placeholder="Enter an IBAN to check for validity..." test-id="iban-input" />
|
<c-input-text v-model:value="rawIban" placeholder="Enter an IBAN to check for validity..." test-id="iban-input" />
|
||||||
|
|
||||||
<c-key-value-list :items="ibanInfo" my-5 />
|
<c-key-value-list :items="ibanInfo" my-5 data-test-id="iban-info" />
|
||||||
|
|
||||||
<c-card title="Valid IBAN examples">
|
<c-card title="Valid IBAN examples">
|
||||||
<div v-for="iban in ibanExamples" :key="iban">
|
<div v-for="iban in ibanExamples" :key="iban">
|
||||||
|
|
27
src/ui/c-key-value-list/c-key-value-list-item.vue
Normal file
27
src/ui/c-key-value-list/c-key-value-list-item.vue
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import _ from 'lodash';
|
||||||
|
import type { CKeyValueListItem } from './c-key-value-list.types';
|
||||||
|
|
||||||
|
const props = defineProps<{ item: CKeyValueListItem }>();
|
||||||
|
const { item } = toRefs(props);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="_.isArray(item.value)">
|
||||||
|
<div v-for="value in item.value" :key="value">
|
||||||
|
<c-text-copyable :value="value" :show-icon="item.showCopyButton ?? true" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="_.isBoolean(item.value)">
|
||||||
|
<c-text-copyable :value="item.value ? 'true' : 'false'" :displayed-value="item.value ? 'Yes' : 'No'" :show-icon="item.showCopyButton ?? true" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="_.isNumber(item.value)" font-mono>
|
||||||
|
<c-text-copyable :value="String(item.value)" :show-icon="item.showCopyButton ?? true" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="_.isNil(item.value) || item.value === ''" op-70>
|
||||||
|
{{ item.placeholder ?? 'N/A' }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<c-text-copyable :value="item.value" :show-icon="item.showCopyButton ?? true" />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -9,29 +9,13 @@ const formattedItems = computed(() => items.value.filter(item => !_.isNil(item.v
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table border-collapse table-fixed>
|
<div my-5>
|
||||||
<tr v-for="item in formattedItems" :key="item.label">
|
<div v-for="item in formattedItems" :key="item.label" flex gap-2 py-1 class="c-key-value-list__item">
|
||||||
<td py-1 pr-2 text-right font-bold>
|
<div flex-basis-180px text-right font-bold class="c-key-value-list__key">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</td>
|
|
||||||
|
|
||||||
<td v-if="_.isArray(item.value)">
|
|
||||||
<div v-for="value in item.value" :key="value">
|
|
||||||
<c-text-copyable :value="value" :show-icon="item.showCopyButton ?? true" />
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
|
||||||
<td v-else-if="_.isBoolean(item.value)">
|
<c-key-value-list-item :item="item" class="c-key-value-list__value" />
|
||||||
<c-text-copyable :value="item.value ? 'true' : 'false'" :displayed-value="item.value ? 'Yes' : 'No'" :show-icon="item.showCopyButton ?? true" />
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td v-else-if="_.isNumber(item.value)" font-mono>
|
|
||||||
<c-text-copyable :value="String(item.value)" :show-icon="item.showCopyButton ?? true" />
|
|
||||||
</td>
|
|
||||||
<td v-else-if="_.isNil(item.value) || item.value === ''" op-70>
|
|
||||||
{{ item.placeholder ?? 'N/A' }}
|
|
||||||
</td>
|
|
||||||
<td v-else>
|
|
||||||
<c-text-copyable :value="item.value" :show-icon="item.showCopyButton ?? true" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -19,7 +19,10 @@ const isTargetHovered = useElementHover(targetRef);
|
||||||
'op-100 scale-100': isTargetHovered,
|
'op-100 scale-100': isTargetHovered,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<slot name="tooltip">
|
<slot
|
||||||
|
v-if="isTargetHovered"
|
||||||
|
name="tooltip"
|
||||||
|
>
|
||||||
{{ tooltip }}
|
{{ tooltip }}
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default defineConfig({
|
||||||
runtimeOnly: true,
|
runtimeOnly: true,
|
||||||
compositionOnly: true,
|
compositionOnly: true,
|
||||||
fullInstall: true,
|
fullInstall: true,
|
||||||
include: [resolve(__dirname, 'locales/**'), resolve(__dirname, 'src/tools/*/locales/**')],
|
include: [resolve(__dirname, 'locales/**')],
|
||||||
}),
|
}),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -106,4 +106,7 @@ export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
exclude: [...configDefaults.exclude, '**/*.e2e.spec.ts'],
|
exclude: [...configDefaults.exclude, '**/*.e2e.spec.ts'],
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
target: 'esnext',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue