feat: add static vs dynamic QR code toggle to the create page
This commit is contained in:
@@ -11,6 +11,7 @@ export default function CreateQRPage() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [destinationUrl, setDestinationUrl] = useState('https://ck-qr.com');
|
const [destinationUrl, setDestinationUrl] = useState('https://ck-qr.com');
|
||||||
const [qrName, setQrName] = useState('');
|
const [qrName, setQrName] = useState('');
|
||||||
|
const [qrType, setQrType] = useState<'dynamic' | 'static'>('dynamic');
|
||||||
const [customSlug, setCustomSlug] = useState('');
|
const [customSlug, setCustomSlug] = useState('');
|
||||||
const [randomSlug, setRandomSlug] = useState('');
|
const [randomSlug, setRandomSlug] = useState('');
|
||||||
const [isGenerating, setIsGenerating] = useState(false);
|
const [isGenerating, setIsGenerating] = useState(false);
|
||||||
@@ -49,9 +50,22 @@ export default function CreateQRPage() {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const generated = Math.random().toString(36).substring(2, 8);
|
const generated = Math.random().toString(36).substring(2, 8);
|
||||||
setRandomSlug(generated);
|
setRandomSlug(generated);
|
||||||
setQrOptions(prev => ({ ...prev, data: `${baseUrl}/l/${generated}` }));
|
if (qrType === 'dynamic') {
|
||||||
|
setQrOptions(prev => ({ ...prev, data: `${baseUrl}/l/${generated}` }));
|
||||||
|
} else {
|
||||||
|
setQrOptions(prev => ({ ...prev, data: destinationUrl }));
|
||||||
|
}
|
||||||
}, [baseUrl]);
|
}, [baseUrl]);
|
||||||
|
|
||||||
|
// Keep QR data in sync with type and destinationUrl
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (qrType === 'static') {
|
||||||
|
setQrOptions(prev => ({ ...prev, data: destinationUrl }));
|
||||||
|
} else {
|
||||||
|
setQrOptions(prev => ({ ...prev, data: `${baseUrl}/l/${customSlug || randomSlug}` }));
|
||||||
|
}
|
||||||
|
}, [qrType, destinationUrl]);
|
||||||
|
|
||||||
// Keep QR data in sync with custom slug
|
// Keep QR data in sync with custom slug
|
||||||
const handleSlugChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSlugChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const val = e.target.value.replace(/[^a-zA-Z0-9-_]/g, '');
|
const val = e.target.value.replace(/[^a-zA-Z0-9-_]/g, '');
|
||||||
@@ -65,8 +79,8 @@ export default function CreateQRPage() {
|
|||||||
// Generate Dynamic QR via API
|
// Generate Dynamic QR via API
|
||||||
const response = await api.post('/qr/generate', {
|
const response = await api.post('/qr/generate', {
|
||||||
name: qrName,
|
name: qrName,
|
||||||
customSlug: customSlug || randomSlug, // Pass the previewed slug so the backend uses it!
|
customSlug: qrType === 'dynamic' ? (customSlug || randomSlug) : undefined, // Pass the previewed slug so the backend uses it!
|
||||||
type: 'dynamic',
|
type: qrType,
|
||||||
dataType: 'URL',
|
dataType: 'URL',
|
||||||
destinationUrl: destinationUrl,
|
destinationUrl: destinationUrl,
|
||||||
designData: qrOptions
|
designData: qrOptions
|
||||||
@@ -134,6 +148,34 @@ export default function CreateQRPage() {
|
|||||||
<h2 className="text-xl font-bold text-gray-800 border-b pb-4 mb-6">QR Content</h2>
|
<h2 className="text-xl font-bold text-gray-800 border-b pb-4 mb-6">QR Content</h2>
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">QR Type</label>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<label className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="qrType"
|
||||||
|
value="dynamic"
|
||||||
|
checked={qrType === 'dynamic'}
|
||||||
|
onChange={() => setQrType('dynamic')}
|
||||||
|
className="text-indigo-600 focus:ring-indigo-500"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-900 font-medium">Dynamic (Trackable & Editable)</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="qrType"
|
||||||
|
value="static"
|
||||||
|
checked={qrType === 'static'}
|
||||||
|
onChange={() => setQrType('static')}
|
||||||
|
className="text-indigo-600 focus:ring-indigo-500"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-900 font-medium">Static (Permanent)</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Internal Name</label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">Internal Name</label>
|
||||||
<input
|
<input
|
||||||
@@ -154,25 +196,32 @@ export default function CreateQRPage() {
|
|||||||
placeholder="https://your-website.com"
|
placeholder="https://your-website.com"
|
||||||
className="w-full border-gray-300 rounded-lg shadow-sm p-3 border focus:ring-indigo-500 focus:border-indigo-500 text-gray-900 bg-white"
|
className="w-full border-gray-300 rounded-lg shadow-sm p-3 border focus:ring-indigo-500 focus:border-indigo-500 text-gray-900 bg-white"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-500 mt-2">This is a dynamic QR. You can change this URL later without reprinting the code.</p>
|
{qrType === 'dynamic' && (
|
||||||
|
<p className="text-xs text-gray-500 mt-2">This is a dynamic QR. You can change this URL later without reprinting the code.</p>
|
||||||
|
)}
|
||||||
|
{qrType === 'static' && (
|
||||||
|
<p className="text-xs text-gray-500 mt-2">This URL is embedded directly into the code and cannot be changed later.</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{qrType === 'dynamic' && (
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Custom Short Link (Optional)</label>
|
<div>
|
||||||
<div className="flex flex-col sm:flex-row shadow-sm rounded-lg overflow-hidden border border-gray-300 focus-within:ring-2 focus-within:ring-indigo-500">
|
<label className="block text-sm font-medium text-gray-700 mb-1">Custom Short Link (Optional)</label>
|
||||||
<span className="inline-flex items-center px-4 bg-gray-50 text-gray-500 border-r border-gray-300 text-sm whitespace-nowrap">
|
<div className="flex flex-col sm:flex-row shadow-sm rounded-lg overflow-hidden border border-gray-300 focus-within:ring-2 focus-within:ring-indigo-500">
|
||||||
{(process.env.NEXT_PUBLIC_APP_URL || 'https://qr.houseofwebsites.ai').replace(/^https?:\/\//, '')}/l/
|
<span className="inline-flex items-center px-4 bg-gray-50 text-gray-500 border-r border-gray-300 text-sm whitespace-nowrap">
|
||||||
</span>
|
{(process.env.NEXT_PUBLIC_APP_URL || 'https://qr.houseofwebsites.ai').replace(/^https?:\/\//, '')}/l/
|
||||||
<input
|
</span>
|
||||||
type="text"
|
<input
|
||||||
value={customSlug}
|
type="text"
|
||||||
onChange={handleSlugChange}
|
value={customSlug}
|
||||||
placeholder={randomSlug || "my-promo"}
|
onChange={handleSlugChange}
|
||||||
className="flex-1 w-full p-3 focus:outline-none text-gray-900 bg-white"
|
placeholder={randomSlug || "my-promo"}
|
||||||
/>
|
className="flex-1 w-full p-3 focus:outline-none text-gray-900 bg-white"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 mt-2">Leave blank to auto-generate a random link.</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500 mt-2">Leave blank to auto-generate a random link.</p>
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user