Skip to content

Commit 48a9245

Browse files
committed
Merge branch 'feat/pin-code-generator' into chore/all-my-stuffs
# Conflicts: # components.d.ts # src/tools/index.ts
2 parents 320a897 + 8e9c5c0 commit 48a9245

File tree

6 files changed

+141
-0
lines changed

6 files changed

+141
-0
lines changed

pnpm-lock.yaml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { tool as mathFormatsConverter } from './math-formats-converter';
3030
import { tool as mathOcr } from './math-ocr';
3131
import { tool as markdownTocGenerator } from './markdown-toc-generator';
3232
import { tool as passphraseGenerator } from './passphrase-generator';
33+
import { tool as pinCodeGenerator } from './pin-code-generator';
3334

3435
import { tool as cssXpathConverter } from './css-xpath-converter';
3536
import { tool as cssSelectorsMemo } from './css-selectors-memo';
@@ -162,6 +163,7 @@ export const toolsByCategory: ToolCategory[] = [
162163
crcCalculator,
163164
fileHasher,
164165
passphraseGenerator,
166+
pinCodeGenerator,
165167
hashText,
166168
bcrypt,
167169
uuidGenerator,
@@ -182,6 +184,8 @@ export const toolsByCategory: ToolCategory[] = [
182184
passwordStrengthAnalyser,
183185
pdfSignatureChecker,
184186
pdfUnlock,
187+
passwordStrengthAnalyser,
188+
pdfSignatureChecker,
185189
],
186190
},
187191
{

src/tools/pin-code-generator/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { FileDigit } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Pin Code Generator',
6+
path: '/pin-code-generator',
7+
description: 'Generate Unique PIN (digits) codes',
8+
keywords: ['pin', 'code', 'digits', 'generator'],
9+
component: () => import('./pin-code-generator.vue'),
10+
icon: FileDigit,
11+
createdAt: new Date('2024-08-15'),
12+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { randomNumber } from './pin-code-generator.service';
3+
4+
describe('pin-code-generator', () => {
5+
describe('randomNumber', () => {
6+
it('should generate random pin code of given length', () => {
7+
expect(randomNumber({
8+
length: 10,
9+
})).toHaveLength(10);
10+
expect(randomNumber({
11+
length: 4,
12+
})).toHaveLength(4);
13+
expect(randomNumber({
14+
length: 25,
15+
})).toHaveLength(25);
16+
});
17+
it('should generate random pin code without duplicate', () => {
18+
const pin = randomNumber({
19+
length: 10,
20+
});
21+
expect(pin).toHaveLength(10);
22+
expect(pin.length).to.eq([...new Set(pin.split(''))].length);
23+
});
24+
25+
it('should generate random pin code with duplicate', () => {
26+
const pin = randomNumber({
27+
length: 12,
28+
});
29+
const uniqueDigits = [...new Set(pin.split(''))];
30+
expect(pin).toHaveLength(12);
31+
expect(pin.length).to.greaterThan(uniqueDigits.length);
32+
expect(uniqueDigits.length).to.lessThanOrEqual(10);
33+
});
34+
});
35+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export function randomNumber({
2+
length,
3+
repeatDigits = false,
4+
}: {
5+
length: number
6+
repeatDigits?: boolean
7+
}) {
8+
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
9+
const result: number[] = [];
10+
const willRepeat = length > 10 || repeatDigits;
11+
12+
while (result.length < length) {
13+
const random = Math.floor(Math.random() * 10); // NOSONAR
14+
const num = arr[random];
15+
if (!willRepeat && result.includes(num)) {
16+
continue;
17+
}
18+
result.push(num);
19+
}
20+
21+
return result.join('');
22+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<script setup lang="ts">
2+
import { randomNumber } from './pin-code-generator.service';
3+
import { useCopy } from '@/composable/copy';
4+
import { useQueryParamOrStorage } from '@/composable/queryParams';
5+
import { computedRefreshable } from '@/composable/computedRefreshable';
6+
7+
const count = useQueryParamOrStorage({ name: 'count', storageName: 'pin-generator:count', defaultValue: 1 });
8+
const length = useQueryParamOrStorage({ name: 'length', storageName: 'pin-generator:length', defaultValue: 6 });
9+
const repeat = useQueryParamOrStorage({ name: 'repeat', storageName: 'pin-generator:repeat', defaultValue: true });
10+
11+
const [pins, refreshPins] = computedRefreshable(() =>
12+
Array.from({ length: count.value },
13+
() => randomNumber({
14+
length: length.value,
15+
repeatDigits: repeat.value,
16+
})).join('\n'),
17+
);
18+
19+
const { copy } = useCopy({ source: pins, text: 'Pin code copied to clipboard!' });
20+
</script>
21+
22+
<template>
23+
<div>
24+
<c-card>
25+
<n-form-item :label="`Number of digits (${length})`" label-placement="left">
26+
<n-slider v-model:value="length" :step="1" :min="1" :max="512" mr-2 />
27+
<n-input-number v-model:value="length" size="small" />
28+
</n-form-item>
29+
30+
<n-form-item label="Allow repeated digits" label-placement="left">
31+
<n-switch v-model:value="repeat" />
32+
</n-form-item>
33+
34+
<n-form-item label="Number of PIN codes to generate" label-placement="left">
35+
<n-input-number v-model:value="count" size="small" />
36+
</n-form-item>
37+
38+
<c-input-text
39+
v-model:value="pins"
40+
multiline
41+
placeholder="PIN codes..."
42+
readonly
43+
rows="3"
44+
autosize
45+
class="pin-code-display"
46+
word-wrap
47+
/>
48+
49+
<div mt-5 flex justify-center gap-3>
50+
<c-button @click="copy()">
51+
Copy
52+
</c-button>
53+
<c-button @click="refreshPins">
54+
Refresh
55+
</c-button>
56+
</div>
57+
</c-card>
58+
</div>
59+
</template>
60+
61+
<style scoped lang="less">
62+
::v-deep(.pin-code-display) {
63+
textarea {
64+
text-align: center;
65+
}
66+
}
67+
</style>

0 commit comments

Comments
 (0)