refactor(mac-address-generator): improved ux
This commit is contained in:
parent
e48fd59cfb
commit
a3efaae04b
@ -1,12 +1,12 @@
|
|||||||
import { Atom2 } from '@vicons/tabler';
|
import { Devices } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'MAC address generator',
|
name: 'MAC address generator',
|
||||||
path: '/mac-address-generator',
|
path: '/mac-address-generator',
|
||||||
description: 'Enter the quantity and prefix. MAC addresses will be generated in your chosen case (uppercase or lowercase)',
|
description: 'Enter the quantity and prefix. MAC addresses will be generated in your chosen case (uppercase or lowercase)',
|
||||||
keywords: ['mac', 'address', 'generator'],
|
keywords: ['mac', 'address', 'generator', 'random', 'prefix'],
|
||||||
component: () => import('./mac-address-generator.vue'),
|
component: () => import('./mac-address-generator.vue'),
|
||||||
icon: Atom2,
|
icon: Devices,
|
||||||
createdAt: new Date('2023-10-11'),
|
createdAt: new Date('2023-11-31'),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,61 +1,92 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { generateRandomMacAddress } from './mac-adress-generator.models';
|
||||||
import { computedRefreshable } from '@/composable/computedRefreshable';
|
import { computedRefreshable } from '@/composable/computedRefreshable';
|
||||||
import { useCopy } from '@/composable/copy';
|
import { useCopy } from '@/composable/copy';
|
||||||
import { partialMacAddressValidation, partialMacAddressValidationRules } from '@/utils/macAddress';
|
import { useValidation } from '@/composable/validation';
|
||||||
|
import { usePartialMacAddressValidation } from '@/utils/macAddress';
|
||||||
|
|
||||||
const amount = useStorage('mac-address-generator-amount', 1);
|
const amount = useStorage('mac-address-generator-amount', 1);
|
||||||
const uppercase = useStorage('mac-address-generator-uppercase', true);
|
|
||||||
const macAddressPrefix = useStorage('mac-address-generator-prefix', '64:16:7F');
|
const macAddressPrefix = useStorage('mac-address-generator-prefix', '64:16:7F');
|
||||||
|
|
||||||
|
const prefixValidation = usePartialMacAddressValidation(macAddressPrefix);
|
||||||
|
|
||||||
|
const casesTransformers = [
|
||||||
|
{ label: 'Uppercase', value: (value: string) => value.toUpperCase() },
|
||||||
|
{ label: 'Lowercase', value: (value: string) => value.toLowerCase() },
|
||||||
|
];
|
||||||
|
const caseTransformer = ref(casesTransformers[0].value);
|
||||||
|
|
||||||
|
const separators = [
|
||||||
|
{
|
||||||
|
label: ':',
|
||||||
|
value: ':',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '-',
|
||||||
|
value: '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '.',
|
||||||
|
value: '.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'None',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const separator = useStorage('mac-address-generator-separator', separators[0].value);
|
||||||
|
|
||||||
const [macAddresses, refreshMacAddresses] = computedRefreshable(() => {
|
const [macAddresses, refreshMacAddresses] = computedRefreshable(() => {
|
||||||
if (!partialMacAddressValidation(macAddressPrefix).isValid) {
|
if (!prefixValidation.isValid) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = _.times(amount.value, () => generateMac(macAddressPrefix.value));
|
const ids = _.times(amount.value, () => caseTransformer.value(generateRandomMacAddress({
|
||||||
|
prefix: macAddressPrefix.value,
|
||||||
|
separator: separator.value,
|
||||||
|
})));
|
||||||
return ids.join('\n');
|
return ids.join('\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
const { copy } = useCopy({ source: macAddresses, text: 'MAC addresses copied to the clipboard' });
|
const { copy } = useCopy({ source: macAddresses, text: 'MAC addresses copied to the clipboard' });
|
||||||
|
|
||||||
function generateMac(prefix: string = ''): string {
|
|
||||||
let mac = prefix;
|
|
||||||
|
|
||||||
for (let i = prefix.length; i < 17; i++) {
|
|
||||||
if (i % 3 === 2) { // Place ':' after every 2 hex characters
|
|
||||||
mac += ':';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mac += Math.floor(Math.random() * 16).toString(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return uppercase.value ? mac.toUpperCase() : mac;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div flex flex-col justify-center gap-2>
|
<div flex flex-col justify-center gap-2>
|
||||||
<div flex items-center>
|
<div flex items-center>
|
||||||
<label w-75px> Quantity:</label>
|
<label w-150px pr-12px text-right> Quantity:</label>
|
||||||
<n-input-number v-model:value="amount" min="1" max="100" flex-1 />
|
<n-input-number v-model:value="amount" min="1" max="100" flex-1 />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<c-input-text
|
<c-input-text
|
||||||
v-model:value="macAddressPrefix"
|
v-model:value="macAddressPrefix"
|
||||||
label="MAC address prefix:"
|
label="MAC address prefix:"
|
||||||
placeholder="Type a MAC address"
|
placeholder="Set a prefix, e.g. 64:16:7F"
|
||||||
clearable
|
clearable
|
||||||
label-position="left"
|
label-position="left"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
:validation-rules="partialMacAddressValidationRules"
|
:validation="prefixValidation"
|
||||||
mb-5
|
raw-text
|
||||||
|
label-width="150px"
|
||||||
|
label-align="right"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n-checkbox v-model:checked="uppercase">
|
<c-buttons-select
|
||||||
Uppercase
|
v-model:value="caseTransformer"
|
||||||
</n-checkbox>
|
:options="casesTransformers"
|
||||||
|
label="Case:"
|
||||||
|
label-width="150px"
|
||||||
|
label-align="right"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<c-buttons-select
|
||||||
|
v-model:value="separator"
|
||||||
|
:options="separators"
|
||||||
|
label="Separator:"
|
||||||
|
label-width="150px"
|
||||||
|
label-align="right"
|
||||||
|
/>
|
||||||
|
|
||||||
<c-card mt-5 flex data-test-id="ulids">
|
<c-card mt-5 flex data-test-id="ulids">
|
||||||
<pre m-0 m-x-auto>{{ macAddresses }}</pre>
|
<pre m-0 m-x-auto>{{ macAddresses }}</pre>
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { generateRandomMacAddress, splitPrefix } from './mac-adress-generator.models';
|
||||||
|
|
||||||
|
describe('mac-adress-generator models', () => {
|
||||||
|
describe('splitPrefix', () => {
|
||||||
|
it('a mac address prefix is splitted around non hex characters', () => {
|
||||||
|
expect(splitPrefix('')).toEqual([]);
|
||||||
|
expect(splitPrefix('01')).toEqual(['01']);
|
||||||
|
expect(splitPrefix('01:')).toEqual(['01']);
|
||||||
|
expect(splitPrefix('01:23')).toEqual(['01', '23']);
|
||||||
|
expect(splitPrefix('01-23')).toEqual(['01', '23']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when a prefix contains only hex characters, they are grouped by 2', () => {
|
||||||
|
expect(splitPrefix('0123')).toEqual(['01', '23']);
|
||||||
|
expect(splitPrefix('012345')).toEqual(['01', '23', '45']);
|
||||||
|
expect(splitPrefix('0123456')).toEqual(['01', '23', '45', '06']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateRandomMacAddress', () => {
|
||||||
|
const createRandomByteGenerator = () => {
|
||||||
|
let i = 0;
|
||||||
|
return () => (i++).toString(16).padStart(2, '0');
|
||||||
|
};
|
||||||
|
|
||||||
|
it('generates a random mac address', () => {
|
||||||
|
expect(generateRandomMacAddress({ getRandomByte: createRandomByteGenerator() })).toBe('00:01:02:03:04:05');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates a random mac address with a prefix', () => {
|
||||||
|
expect(generateRandomMacAddress({ prefix: 'ff:ee:aa', getRandomByte: createRandomByteGenerator() })).toBe('ff:ee:aa:00:01:02');
|
||||||
|
expect(generateRandomMacAddress({ prefix: 'ff:ee:a', getRandomByte: createRandomByteGenerator() })).toBe('ff:ee:0a:00:01:02');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates a random mac address with a prefix and a different separator', () => {
|
||||||
|
expect(generateRandomMacAddress({ prefix: 'ff-ee-aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
|
||||||
|
expect(generateRandomMacAddress({ prefix: 'ff:ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
|
||||||
|
expect(generateRandomMacAddress({ prefix: 'ff-ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
|
||||||
|
expect(generateRandomMacAddress({ prefix: 'ff ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export { splitPrefix, generateRandomMacAddress };
|
||||||
|
|
||||||
|
function splitPrefix(prefix: string): string[] {
|
||||||
|
const base = prefix.match(/[^0-9a-f]/i) === null ? prefix.match(/.{1,2}/g) ?? [] : prefix.split(/[^0-9a-f]/i);
|
||||||
|
|
||||||
|
return base.filter(Boolean).map(byte => byte.padStart(2, '0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRandomMacAddress({ prefix: rawPrefix = '', separator = ':', getRandomByte = () => _.random(0, 255).toString(16).padStart(2, '0') }: { prefix?: string; separator?: string; getRandomByte?: () => string } = {}) {
|
||||||
|
const prefix = splitPrefix(rawPrefix);
|
||||||
|
|
||||||
|
const randomBytes = _.times(6 - prefix.length, getRandomByte);
|
||||||
|
const bytes = [...prefix, ...randomBytes];
|
||||||
|
|
||||||
|
return bytes.join(separator);
|
||||||
|
}
|
||||||
@ -18,15 +18,15 @@ function macAddressValidation(value: Ref) {
|
|||||||
const partialMacAddressValidationRules = [
|
const partialMacAddressValidationRules = [
|
||||||
{
|
{
|
||||||
message: 'Invalid partial MAC address',
|
message: 'Invalid partial MAC address',
|
||||||
validator: (value: string) => value.trim().match(/^([0-9A-Fa-f]{2}[:-]){0,5}([0-9A-Fa-f]{0,2})$/),
|
validator: (value: string) => value.trim().match(/^([0-9a-f]{2}[:\-. ]){0,5}([0-9a-f]{0,2})$/i),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function partialMacAddressValidation(value: Ref) {
|
function usePartialMacAddressValidation(value: Ref) {
|
||||||
return useValidation({
|
return useValidation({
|
||||||
source: value,
|
source: value,
|
||||||
rules: partialMacAddressValidationRules,
|
rules: partialMacAddressValidationRules,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { macAddressValidation, macAddressValidationRules, partialMacAddressValidation, partialMacAddressValidationRules };
|
export { macAddressValidation, macAddressValidationRules, usePartialMacAddressValidation, partialMacAddressValidationRules };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user