mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-19 19:44:05 +01:00
feat: qr code generation init
This commit is contained in:
106
.idea/workspace.xml
generated
106
.idea/workspace.xml
generated
@@ -5,7 +5,13 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: compute flow">
|
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: compute flow">
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/qr-code/index.tsx" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/qr-code/meta.ts" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/qr-code/types.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/generic/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/index.ts" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -22,7 +28,7 @@
|
|||||||
<option name="PUSH_AUTO_UPDATE" value="true" />
|
<option name="PUSH_AUTO_UPDATE" value="true" />
|
||||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||||
<map>
|
<map>
|
||||||
<entry key="$PROJECT_DIR$" value="chesterkxng" />
|
<entry key="$PROJECT_DIR$" value="fork/m5lk3n/feature/base64" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
@@ -199,56 +205,56 @@
|
|||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
<option name="showLibraryContents" value="true" />
|
<option name="showLibraryContents" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent">{
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
"keyToString": {
|
"keyToString": {
|
||||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||||
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||||
"Docker.Dockerfile build.executor": "Run",
|
"Docker.Dockerfile build.executor": "Run",
|
||||||
"Docker.Dockerfile.executor": "Run",
|
"Docker.Dockerfile.executor": "Run",
|
||||||
"Playwright.Create transparent PNG.should make png color transparent.executor": "Run",
|
"Playwright.Create transparent PNG.should make png color transparent.executor": "Run",
|
||||||
"Playwright.JoinText Component.executor": "Run",
|
"Playwright.JoinText Component.executor": "Run",
|
||||||
"Playwright.JoinText Component.should merge text pieces with specified join character.executor": "Run",
|
"Playwright.JoinText Component.should merge text pieces with specified join character.executor": "Run",
|
||||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
"Vitest.compute function (1).executor": "Run",
|
"Vitest.compute function (1).executor": "Run",
|
||||||
"Vitest.compute function.executor": "Run",
|
"Vitest.compute function.executor": "Run",
|
||||||
"Vitest.mergeText.executor": "Run",
|
"Vitest.mergeText.executor": "Run",
|
||||||
"Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor": "Run",
|
"Vitest.mergeText.should merge lines and preserve blank lines when deleteBlankLines is false.executor": "Run",
|
||||||
"Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.executor": "Run",
|
"Vitest.mergeText.should merge lines, preserve blank lines and trailing spaces when both deleteBlankLines and deleteTrailingSpaces are false.executor": "Run",
|
||||||
"Vitest.parsePageRanges.executor": "Run",
|
"Vitest.parsePageRanges.executor": "Run",
|
||||||
"Vitest.removeDuplicateLines function.executor": "Run",
|
"Vitest.removeDuplicateLines function.executor": "Run",
|
||||||
"Vitest.removeDuplicateLines function.newlines option.executor": "Run",
|
"Vitest.removeDuplicateLines function.newlines option.executor": "Run",
|
||||||
"Vitest.removeDuplicateLines function.newlines option.should filter newlines when newlines is set to filter.executor": "Run",
|
"Vitest.removeDuplicateLines function.newlines option.should filter newlines when newlines is set to filter.executor": "Run",
|
||||||
"Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp.executor": "Run",
|
"Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp.executor": "Run",
|
||||||
"Vitest.replaceText function.executor": "Run",
|
"Vitest.replaceText function.executor": "Run",
|
||||||
"Vitest.timeBetweenDates.executor": "Run",
|
"Vitest.timeBetweenDates.executor": "Run",
|
||||||
"git-widget-placeholder": "#131 on fork/ARRY7686/main",
|
"git-widget-placeholder": "main",
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
"kotlin-language-version-configured": "true",
|
"kotlin-language-version-configured": "true",
|
||||||
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/src",
|
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/src",
|
||||||
"node.js.detected.package.eslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.tslint": "true",
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
"node.js.selected.package.tslint": "(autodetect)",
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
"nodejs_package_manager_path": "npm",
|
"nodejs_package_manager_path": "npm",
|
||||||
"npm.build.executor": "Run",
|
"npm.build.executor": "Run",
|
||||||
"npm.dev.executor": "Run",
|
"npm.dev.executor": "Run",
|
||||||
"npm.lint.executor": "Run",
|
"npm.lint.executor": "Run",
|
||||||
"npm.prebuild.executor": "Run",
|
"npm.prebuild.executor": "Run",
|
||||||
"npm.script:create:tool.executor": "Run",
|
"npm.script:create:tool.executor": "Run",
|
||||||
"npm.test.executor": "Run",
|
"npm.test.executor": "Run",
|
||||||
"npm.test:e2e.executor": "Run",
|
"npm.test:e2e.executor": "Run",
|
||||||
"npm.test:e2e:run.executor": "Run",
|
"npm.test:e2e:run.executor": "Run",
|
||||||
"prettierjs.PrettierConfiguration.Package": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\prettier",
|
"prettierjs.PrettierConfiguration.Package": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\prettier",
|
||||||
"project.structure.last.edited": "Problems",
|
"project.structure.last.edited": "Problems",
|
||||||
"project.structure.proportion": "0.0",
|
"project.structure.proportion": "0.0",
|
||||||
"project.structure.side.proportion": "0.2",
|
"project.structure.side.proportion": "0.2",
|
||||||
"settings.editor.selected.configurable": "refactai_advanced_settings",
|
"settings.editor.selected.configurable": "refactai_advanced_settings",
|
||||||
"ts.external.directory.path": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib",
|
"ts.external.directory.path": "C:\\Users\\Ibrahima\\IdeaProjects\\omni-tools\\node_modules\\typescript\\lib",
|
||||||
"vue.rearranger.settings.migration": "true"
|
"vue.rearranger.settings.migration": "true"
|
||||||
}
|
}
|
||||||
}</component>
|
}]]></component>
|
||||||
<component name="ReactDesignerToolWindowState">
|
<component name="ReactDesignerToolWindowState">
|
||||||
<option name="myId2Visible">
|
<option name="myId2Visible">
|
||||||
<map>
|
<map>
|
||||||
|
|||||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -41,6 +41,7 @@
|
|||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"pdfjs-dist": "^5.2.133",
|
"pdfjs-dist": "^5.2.133",
|
||||||
"playwright": "^1.45.0",
|
"playwright": "^1.45.0",
|
||||||
|
"qrcode.react": "^4.2.0",
|
||||||
"rc-slider": "^11.1.8",
|
"rc-slider": "^11.1.8",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -9129,6 +9130,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode.react": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"pdfjs-dist": "^5.2.133",
|
"pdfjs-dist": "^5.2.133",
|
||||||
"playwright": "^1.45.0",
|
"playwright": "^1.45.0",
|
||||||
|
"qrcode.react": "^4.2.0",
|
||||||
"rc-slider": "^11.1.8",
|
"rc-slider": "^11.1.8",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { tool as cropImage } from './crop/meta';
|
|||||||
import { tool as changeOpacity } from './change-opacity/meta';
|
import { tool as changeOpacity } from './change-opacity/meta';
|
||||||
import { tool as createTransparent } from './create-transparent/meta';
|
import { tool as createTransparent } from './create-transparent/meta';
|
||||||
import { tool as imageToText } from './image-to-text/meta';
|
import { tool as imageToText } from './image-to-text/meta';
|
||||||
|
import { tool as qrCodeGenerator } from './qr-code/meta';
|
||||||
|
|
||||||
export const imageGenericTools = [
|
export const imageGenericTools = [
|
||||||
resizeImage,
|
resizeImage,
|
||||||
@@ -15,5 +16,6 @@ export const imageGenericTools = [
|
|||||||
changeOpacity,
|
changeOpacity,
|
||||||
changeColors,
|
changeColors,
|
||||||
createTransparent,
|
createTransparent,
|
||||||
imageToText
|
imageToText,
|
||||||
|
qrCodeGenerator
|
||||||
];
|
];
|
||||||
|
|||||||
516
src/pages/tools/image/generic/qr-code/index.tsx
Normal file
516
src/pages/tools/image/generic/qr-code/index.tsx
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Box, Button, MenuItem, TextField, Typography } from '@mui/material';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
|
import ToolContent from '@components/ToolContent';
|
||||||
|
import { ToolComponentProps } from '@tools/defineTool';
|
||||||
|
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||||
|
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
|
||||||
|
import { InitialValuesType, QRCodeType, WifiEncryptionType } from './types';
|
||||||
|
import ColorSelector from '@components/options/ColorSelector';
|
||||||
|
|
||||||
|
const initialValues: InitialValuesType = {
|
||||||
|
qrCodeType: 'URL',
|
||||||
|
|
||||||
|
// Common settings
|
||||||
|
size: '200',
|
||||||
|
bgColor: '#FFFFFF',
|
||||||
|
fgColor: '#000000',
|
||||||
|
|
||||||
|
// URL
|
||||||
|
url: 'https://example.com',
|
||||||
|
|
||||||
|
// Text
|
||||||
|
text: '',
|
||||||
|
|
||||||
|
// Email
|
||||||
|
emailAddress: '',
|
||||||
|
emailSubject: '',
|
||||||
|
emailBody: '',
|
||||||
|
|
||||||
|
// Phone
|
||||||
|
phoneNumber: '',
|
||||||
|
|
||||||
|
// SMS
|
||||||
|
smsNumber: '',
|
||||||
|
smsMessage: '',
|
||||||
|
|
||||||
|
// WiFi
|
||||||
|
wifiSsid: '',
|
||||||
|
wifiPassword: '',
|
||||||
|
wifiEncryption: 'WPA/WPA2',
|
||||||
|
|
||||||
|
// vCard
|
||||||
|
vCardName: '',
|
||||||
|
vCardEmail: '',
|
||||||
|
vCardPhone: '',
|
||||||
|
vCardAddress: '',
|
||||||
|
vCardCompany: '',
|
||||||
|
vCardTitle: '',
|
||||||
|
vCardWebsite: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to format the QR code data based on the type
|
||||||
|
const formatQRCodeData = (values: InitialValuesType): string => {
|
||||||
|
switch (values.qrCodeType) {
|
||||||
|
case 'URL':
|
||||||
|
return values.url;
|
||||||
|
|
||||||
|
case 'Text':
|
||||||
|
return values.text;
|
||||||
|
|
||||||
|
case 'Email': {
|
||||||
|
let emailData = `mailto:${values.emailAddress}`;
|
||||||
|
if (values.emailSubject || values.emailBody) {
|
||||||
|
emailData += '?';
|
||||||
|
if (values.emailSubject) {
|
||||||
|
emailData += `subject=${encodeURIComponent(values.emailSubject)}`;
|
||||||
|
}
|
||||||
|
if (values.emailBody) {
|
||||||
|
emailData += `${
|
||||||
|
values.emailSubject ? '&' : ''
|
||||||
|
}body=${encodeURIComponent(values.emailBody)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emailData;
|
||||||
|
}
|
||||||
|
case 'Phone':
|
||||||
|
return `tel:${values.phoneNumber}`;
|
||||||
|
|
||||||
|
case 'SMS':
|
||||||
|
return `sms:${values.smsNumber}${
|
||||||
|
values.smsMessage
|
||||||
|
? `?body=${encodeURIComponent(values.smsMessage)}`
|
||||||
|
: ''
|
||||||
|
}`;
|
||||||
|
|
||||||
|
case 'WiFi': {
|
||||||
|
const encryption =
|
||||||
|
values.wifiEncryption === 'None' ? 'nopass' : values.wifiEncryption;
|
||||||
|
return `WIFI:T:${encryption};S:${values.wifiSsid};P:${values.wifiPassword};;`;
|
||||||
|
}
|
||||||
|
case 'vCard':
|
||||||
|
return `BEGIN:VCARD
|
||||||
|
VERSION:3.0
|
||||||
|
N:${values.vCardName}
|
||||||
|
FN:${values.vCardName}
|
||||||
|
ORG:${values.vCardCompany}
|
||||||
|
TITLE:${values.vCardTitle}
|
||||||
|
TEL:${values.vCardPhone}
|
||||||
|
EMAIL:${values.vCardEmail}
|
||||||
|
ADR:${values.vCardAddress}
|
||||||
|
URL:${values.vCardWebsite}
|
||||||
|
END:VCARD`;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
qrCodeType: Yup.string().required('QR code type is required'),
|
||||||
|
size: Yup.number()
|
||||||
|
.min(100, 'Size must be at least 100px')
|
||||||
|
.max(1000, 'Size must be at most 1000px')
|
||||||
|
.required('Size is required'),
|
||||||
|
bgColor: Yup.string().required('Background color is required'),
|
||||||
|
fgColor: Yup.string().required('Foreground color is required'),
|
||||||
|
|
||||||
|
// URL
|
||||||
|
url: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'URL',
|
||||||
|
then: (schema) =>
|
||||||
|
schema.url('Please enter a valid URL').required('URL is required')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Text
|
||||||
|
text: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'Text',
|
||||||
|
then: (schema) => schema.required('Text is required')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Email
|
||||||
|
emailAddress: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'Email',
|
||||||
|
then: (schema) =>
|
||||||
|
schema
|
||||||
|
.email('Please enter a valid email address')
|
||||||
|
.required('Email address is required')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Phone
|
||||||
|
phoneNumber: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'Phone',
|
||||||
|
then: (schema) => schema.required('Phone number is required')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// SMS
|
||||||
|
smsNumber: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'SMS',
|
||||||
|
then: (schema) => schema.required('Phone number is required')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// WiFi
|
||||||
|
wifiSsid: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'WiFi',
|
||||||
|
then: (schema) => schema.required('SSID is required')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// vCard
|
||||||
|
vCardName: Yup.string().when('qrCodeType', {
|
||||||
|
is: 'vCard',
|
||||||
|
then: (schema) => schema.required('Name is required')
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function QRCodeGenerator({ title }: ToolComponentProps) {
|
||||||
|
const [qrValue, setQRValue] = useState<string>('');
|
||||||
|
const [currentValues, setCurrentValues] =
|
||||||
|
useState<InitialValuesType>(initialValues);
|
||||||
|
|
||||||
|
// Update QR code value when form values change
|
||||||
|
useEffect(() => {
|
||||||
|
setQRValue(formatQRCodeData(currentValues));
|
||||||
|
}, [currentValues]);
|
||||||
|
|
||||||
|
const getGroups: GetGroupsType<InitialValuesType> = ({
|
||||||
|
values,
|
||||||
|
updateField
|
||||||
|
}) => {
|
||||||
|
// Update current values for QR code preview
|
||||||
|
setCurrentValues(values);
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'QR Code Type',
|
||||||
|
component: (
|
||||||
|
<Box>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
value={values.qrCodeType}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateField('qrCodeType', e.target.value as QRCodeType)
|
||||||
|
}
|
||||||
|
label="Select QR Code Type"
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
<MenuItem value="URL">URL</MenuItem>
|
||||||
|
<MenuItem value="Text">Text</MenuItem>
|
||||||
|
<MenuItem value="Email">Email</MenuItem>
|
||||||
|
<MenuItem value="Phone">Phone</MenuItem>
|
||||||
|
<MenuItem value="SMS">SMS</MenuItem>
|
||||||
|
<MenuItem value="WiFi">WiFi</MenuItem>
|
||||||
|
<MenuItem value="vCard">vCard (Contact)</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'QR Code Settings',
|
||||||
|
component: (
|
||||||
|
<Box>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.size}
|
||||||
|
onOwnChange={(val) => updateField('size', val)}
|
||||||
|
description="Size in pixels (100-1000)"
|
||||||
|
inputProps={{
|
||||||
|
type: 'number',
|
||||||
|
min: 100,
|
||||||
|
max: 1000
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ColorSelector
|
||||||
|
description="Background Color"
|
||||||
|
value={values.bgColor}
|
||||||
|
onColorChange={(val) => updateField('bgColor', val)}
|
||||||
|
/>
|
||||||
|
<ColorSelector
|
||||||
|
description="Foreground Color"
|
||||||
|
value={values.fgColor}
|
||||||
|
onColorChange={(val) => updateField('fgColor', val)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// Dynamic form fields based on QR code type
|
||||||
|
{
|
||||||
|
title: `${values.qrCodeType} Details`,
|
||||||
|
component: (
|
||||||
|
<Box>
|
||||||
|
{values.qrCodeType === 'URL' && (
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.url}
|
||||||
|
onOwnChange={(val) => updateField('url', val)}
|
||||||
|
description="Enter the URL"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'https://example.com'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.qrCodeType === 'Text' && (
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.text}
|
||||||
|
onOwnChange={(val) => updateField('text', val)}
|
||||||
|
description="Enter the text"
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Enter your text here'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.qrCodeType === 'Email' && (
|
||||||
|
<>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.emailAddress}
|
||||||
|
onOwnChange={(val) => updateField('emailAddress', val)}
|
||||||
|
description="Email Address"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'example@example.com',
|
||||||
|
type: 'email'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.emailSubject}
|
||||||
|
onOwnChange={(val) => updateField('emailSubject', val)}
|
||||||
|
description="Email Subject (optional)"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Subject line'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.emailBody}
|
||||||
|
onOwnChange={(val) => updateField('emailBody', val)}
|
||||||
|
description="Email Body (optional)"
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Body text'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.qrCodeType === 'Phone' && (
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.phoneNumber}
|
||||||
|
onOwnChange={(val) => updateField('phoneNumber', val)}
|
||||||
|
description="Phone Number"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: '+1234567890',
|
||||||
|
type: 'tel'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.qrCodeType === 'SMS' && (
|
||||||
|
<>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.smsNumber}
|
||||||
|
onOwnChange={(val) => updateField('smsNumber', val)}
|
||||||
|
description="Phone Number"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: '+1234567890',
|
||||||
|
type: 'tel'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.smsMessage}
|
||||||
|
onOwnChange={(val) => updateField('smsMessage', val)}
|
||||||
|
description="Message (optional)"
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Your message here'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.qrCodeType === 'WiFi' && (
|
||||||
|
<>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.wifiSsid}
|
||||||
|
onOwnChange={(val) => updateField('wifiSsid', val)}
|
||||||
|
description="Network Name (SSID)"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Network name'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.wifiPassword}
|
||||||
|
onOwnChange={(val) => updateField('wifiPassword', val)}
|
||||||
|
description="Password"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Password',
|
||||||
|
type: 'password'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
value={values.wifiEncryption}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateField(
|
||||||
|
'wifiEncryption',
|
||||||
|
e.target.value as WifiEncryptionType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
label="Encryption Type"
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
<MenuItem value="WPA/WPA2">WPA/WPA2</MenuItem>
|
||||||
|
<MenuItem value="WEP">WEP</MenuItem>
|
||||||
|
<MenuItem value="None">None</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.qrCodeType === 'vCard' && (
|
||||||
|
<>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardName}
|
||||||
|
onOwnChange={(val) => updateField('vCardName', val)}
|
||||||
|
description="Full Name"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'John Doe'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardEmail}
|
||||||
|
onOwnChange={(val) => updateField('vCardEmail', val)}
|
||||||
|
description="Email"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'john@example.com',
|
||||||
|
type: 'email'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardPhone}
|
||||||
|
onOwnChange={(val) => updateField('vCardPhone', val)}
|
||||||
|
description="Phone"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: '+1234567890',
|
||||||
|
type: 'tel'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardAddress}
|
||||||
|
onOwnChange={(val) => updateField('vCardAddress', val)}
|
||||||
|
description="Address"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: '123 Main St, City, Country'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardCompany}
|
||||||
|
onOwnChange={(val) => updateField('vCardCompany', val)}
|
||||||
|
description="Company (optional)"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Company name'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardTitle}
|
||||||
|
onOwnChange={(val) => updateField('vCardTitle', val)}
|
||||||
|
description="Job Title (optional)"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'Software Developer'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextFieldWithDesc
|
||||||
|
value={values.vCardWebsite}
|
||||||
|
onOwnChange={(val) => updateField('vCardWebsite', val)}
|
||||||
|
description="Website (optional)"
|
||||||
|
inputProps={{
|
||||||
|
placeholder: 'https://example.com'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save QR code as image
|
||||||
|
const saveQRCode = () => {
|
||||||
|
const svg = document.getElementById('qr-code-svg');
|
||||||
|
if (!svg) return;
|
||||||
|
|
||||||
|
const svgData = new XMLSerializer().serializeToString(svg);
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
ctx?.drawImage(img, 0, 0);
|
||||||
|
const pngFile = canvas.toDataURL('image/png');
|
||||||
|
|
||||||
|
const downloadLink = document.createElement('a');
|
||||||
|
downloadLink.download = 'qrcode.png';
|
||||||
|
downloadLink.href = pngFile;
|
||||||
|
downloadLink.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src =
|
||||||
|
'data:image/svg+xml;base64,' +
|
||||||
|
btoa(unescape(encodeURIComponent(svgData)));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolContent
|
||||||
|
title={title}
|
||||||
|
initialValues={initialValues}
|
||||||
|
getGroups={getGroups}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
compute={() => {}}
|
||||||
|
inputComponent={
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6">QR Code Preview</Typography>
|
||||||
|
<Box sx={{ border: '1px solid #ddd', padding: 2, borderRadius: 1 }}>
|
||||||
|
{qrValue && (
|
||||||
|
<QRCodeSVG
|
||||||
|
id="qr-code-svg"
|
||||||
|
value={qrValue}
|
||||||
|
size={Number(currentValues.size) || 200}
|
||||||
|
bgColor={currentValues.bgColor}
|
||||||
|
fgColor={currentValues.fgColor}
|
||||||
|
level="H"
|
||||||
|
includeMargin
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={saveQRCode}
|
||||||
|
disabled={!qrValue}
|
||||||
|
>
|
||||||
|
Download QR Code
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
resultComponent={null}
|
||||||
|
toolInfo={{
|
||||||
|
title: 'QR Code Generator',
|
||||||
|
description:
|
||||||
|
'Generate QR codes for different data types: URL, Text, Email, Phone, SMS, WiFi, vCard, and more. Customize the size and colors to create the perfect QR code for your needs.'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
25
src/pages/tools/image/generic/qr-code/meta.ts
Normal file
25
src/pages/tools/image/generic/qr-code/meta.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { defineTool } from '@tools/defineTool';
|
||||||
|
import { lazy } from 'react';
|
||||||
|
|
||||||
|
export const tool = defineTool('image-generic', {
|
||||||
|
name: 'QR Code Generator',
|
||||||
|
path: 'qr-code',
|
||||||
|
icon: 'mdi:qrcode', // Iconify icon as a string
|
||||||
|
description:
|
||||||
|
'Generate QR codes for different data types: URL, Text, Email, Phone, SMS, WiFi, vCard, and more.',
|
||||||
|
shortDescription: 'Create customized QR codes for various data formats.',
|
||||||
|
keywords: [
|
||||||
|
'qr code',
|
||||||
|
'qrcode',
|
||||||
|
'generator',
|
||||||
|
'url',
|
||||||
|
'text',
|
||||||
|
'email',
|
||||||
|
'phone',
|
||||||
|
'sms',
|
||||||
|
'wifi',
|
||||||
|
'vcard',
|
||||||
|
'contact'
|
||||||
|
],
|
||||||
|
component: lazy(() => import('./index'))
|
||||||
|
});
|
||||||
51
src/pages/tools/image/generic/qr-code/types.ts
Normal file
51
src/pages/tools/image/generic/qr-code/types.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
export type QRCodeType =
|
||||||
|
| 'URL'
|
||||||
|
| 'Text'
|
||||||
|
| 'Email'
|
||||||
|
| 'Phone'
|
||||||
|
| 'SMS'
|
||||||
|
| 'WiFi'
|
||||||
|
| 'vCard';
|
||||||
|
|
||||||
|
export type WifiEncryptionType = 'WPA/WPA2' | 'WEP' | 'None';
|
||||||
|
|
||||||
|
export interface InitialValuesType {
|
||||||
|
qrCodeType: QRCodeType;
|
||||||
|
|
||||||
|
// Common settings
|
||||||
|
size: string;
|
||||||
|
bgColor: string;
|
||||||
|
fgColor: string;
|
||||||
|
|
||||||
|
// URL
|
||||||
|
url: string;
|
||||||
|
|
||||||
|
// Text
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
// Email
|
||||||
|
emailAddress: string;
|
||||||
|
emailSubject: string;
|
||||||
|
emailBody: string;
|
||||||
|
|
||||||
|
// Phone
|
||||||
|
phoneNumber: string;
|
||||||
|
|
||||||
|
// SMS
|
||||||
|
smsNumber: string;
|
||||||
|
smsMessage: string;
|
||||||
|
|
||||||
|
// WiFi
|
||||||
|
wifiSsid: string;
|
||||||
|
wifiPassword: string;
|
||||||
|
wifiEncryption: WifiEncryptionType;
|
||||||
|
|
||||||
|
// vCard
|
||||||
|
vCardName: string;
|
||||||
|
vCardEmail: string;
|
||||||
|
vCardPhone: string;
|
||||||
|
vCardAddress: string;
|
||||||
|
vCardCompany: string;
|
||||||
|
vCardTitle: string;
|
||||||
|
vCardWebsite: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user