feat: implement pricing strategy, subscription tiers, and core infrastructure for QR code management
This commit is contained in:
@@ -15,8 +15,9 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useCsrf } from '@/hooks/useCsrf';
|
||||
import { showToast } from '@/components/ui/Toast';
|
||||
import {
|
||||
Globe, User, MapPin, Phone, FileText, Smartphone, Ticket, Star, HelpCircle, Upload
|
||||
Globe, User, MapPin, Phone, FileText, Smartphone, Ticket, Star, HelpCircle, Upload, Barcode as BarcodeIcon
|
||||
} from 'lucide-react';
|
||||
import Barcode from 'react-barcode';
|
||||
|
||||
// Tooltip component for form field help
|
||||
const Tooltip = ({ text }: { text: string }) => (
|
||||
@@ -140,6 +141,7 @@ export default function CreatePage() {
|
||||
{ value: 'APP', label: 'App Download', icon: Smartphone },
|
||||
{ value: 'COUPON', label: 'Coupon / Discount', icon: Ticket },
|
||||
{ value: 'FEEDBACK', label: 'Feedback / Review', icon: Star },
|
||||
{ value: 'BARCODE', label: 'Barcode', icon: BarcodeIcon },
|
||||
];
|
||||
|
||||
// Get QR content based on content type
|
||||
@@ -170,6 +172,8 @@ export default function CreatePage() {
|
||||
return `Coupon: ${content.code || 'SAVE20'} - ${content.discount || '20% OFF'}`;
|
||||
case 'FEEDBACK':
|
||||
return content.feedbackUrl || 'https://example.com/feedback';
|
||||
case 'BARCODE':
|
||||
return content.value || '123456789';
|
||||
default:
|
||||
return 'https://example.com';
|
||||
}
|
||||
@@ -642,6 +646,68 @@ export default function CreatePage() {
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case 'BARCODE':
|
||||
return (
|
||||
<>
|
||||
{isDynamic ? (
|
||||
<>
|
||||
<div className="rounded-lg bg-blue-50 border border-blue-200 p-3 text-sm text-blue-800">
|
||||
<strong>How dynamic barcodes work:</strong> The barcode encodes a short redirect URL
|
||||
(e.g. <span className="font-mono text-xs">qrmaster.net/r/…</span>). When scanned with a
|
||||
smartphone camera, it opens the browser and redirects to your destination — which you
|
||||
can update anytime. Works with smartphone cameras, not POS laser scanners.
|
||||
</div>
|
||||
<Input
|
||||
label="Destination URL"
|
||||
value={content.url || ''}
|
||||
onChange={(e) => setContent({ ...content, url: e.target.value })}
|
||||
placeholder="https://example.com"
|
||||
required
|
||||
/>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Barcode Format</label>
|
||||
<select
|
||||
value={['CODE128', 'CODE39'].includes(content.format) ? content.format : 'CODE128'}
|
||||
onChange={(e) => setContent({ ...content, format: e.target.value })}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
>
|
||||
<option value="CODE128">CODE128 — General purpose (recommended)</option>
|
||||
<option value="CODE39">CODE39 — Industrial / logistics</option>
|
||||
</select>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Only URL-capable formats available. EAN-13, UPC, and ITF-14 encode numbers only and cannot embed a redirect URL.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Input
|
||||
label="Barcode Value"
|
||||
value={content.value || ''}
|
||||
onChange={(e) => setContent({ ...content, value: e.target.value })}
|
||||
placeholder="123456789012"
|
||||
required
|
||||
/>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Barcode Format</label>
|
||||
<select
|
||||
value={content.format || 'CODE128'}
|
||||
onChange={(e) => setContent({ ...content, format: e.target.value })}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
>
|
||||
<option value="CODE128">CODE128 — General purpose (recommended)</option>
|
||||
<option value="EAN13">EAN-13 — Retail products (international)</option>
|
||||
<option value="UPC">UPC — Retail products (USA/Canada)</option>
|
||||
<option value="CODE39">CODE39 — Industrial / logistics</option>
|
||||
<option value="ITF14">ITF-14 — Shipping containers</option>
|
||||
<option value="MSI">MSI — Shelf labeling / inventory</option>
|
||||
<option value="pharmacode">Pharmacode — Pharmaceutical packaging</option>
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -992,7 +1058,25 @@ export default function CreatePage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{qrContent ? (
|
||||
{contentType === 'BARCODE' ? (
|
||||
qrContent ? (
|
||||
<div className="p-2 bg-white">
|
||||
<Barcode
|
||||
value={qrContent}
|
||||
format={content.format || 'CODE128'}
|
||||
lineColor={foregroundColor}
|
||||
background={backgroundColor}
|
||||
width={2}
|
||||
height={100}
|
||||
displayValue={true}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-[200px] h-[200px] bg-gray-100 rounded flex items-center justify-center text-gray-500">
|
||||
Enter barcode value
|
||||
</div>
|
||||
)
|
||||
) : qrContent ? (
|
||||
<div className={cornerStyle === 'rounded' ? 'rounded-lg overflow-hidden' : ''}>
|
||||
<QRCodeSVG
|
||||
value={qrContent}
|
||||
|
||||
Reference in New Issue
Block a user