fix: Fix syntax errors introduced by shell interpolation in dashboard and edit pages
This commit is contained in:
@@ -26,7 +26,7 @@ export default function EditQRPage() {
|
|||||||
width: 300,
|
width: 300,
|
||||||
height: 300,
|
height: 300,
|
||||||
type: 'svg',
|
type: 'svg',
|
||||||
data: $baseUrl/l/preview,
|
data: `${baseUrl}/l/preview`,
|
||||||
margin: 10,
|
margin: 10,
|
||||||
qrOptions: { typeNumber: 0, mode: 'Byte', errorCorrectionLevel: 'Q' },
|
qrOptions: { typeNumber: 0, mode: 'Byte', errorCorrectionLevel: 'Q' },
|
||||||
imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 5 },
|
imageOptions: { hideBackgroundDots: true, imageSize: 0.4, margin: 5 },
|
||||||
@@ -44,7 +44,7 @@ export default function EditQRPage() {
|
|||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
|
||||||
// Fetch existing QR Data
|
// Fetch existing QR Data
|
||||||
api.get(/qr/ + qrId).then(res => {
|
api.get(`/qr/${qrId}`).then(res => {
|
||||||
const qr = res.data.qr_code;
|
const qr = res.data.qr_code;
|
||||||
setQrName(qr.name);
|
setQrName(qr.name);
|
||||||
setDestinationUrl(qr.destination_url);
|
setDestinationUrl(qr.destination_url);
|
||||||
@@ -54,7 +54,7 @@ export default function EditQRPage() {
|
|||||||
if (qr.design_data) {
|
if (qr.design_data) {
|
||||||
setQrOptions({
|
setQrOptions({
|
||||||
...qr.design_data,
|
...qr.design_data,
|
||||||
data: qr.type === 'static' ? qr.destination_url : $baseUrl/l/ + qr.short_url_id
|
data: qr.type === 'static' ? qr.destination_url : `${baseUrl}/l/${qr.short_url_id}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -70,13 +70,13 @@ export default function EditQRPage() {
|
|||||||
if (qrType === 'static') return;
|
if (qrType === 'static') return;
|
||||||
const val = e.target.value.replace(/[^a-zA-Z0-9-_]/g, '');
|
const val = e.target.value.replace(/[^a-zA-Z0-9-_]/g, '');
|
||||||
setCustomSlug(val);
|
setCustomSlug(val);
|
||||||
setQrOptions(prev => ({ ...prev, data: $baseUrl/l/ + val }));
|
setQrOptions(prev => ({ ...prev, data: `${baseUrl}/l/${val}` }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
await api.put(/qr/ + qrId, {
|
await api.put(`/qr/${qrId}`, {
|
||||||
name: qrName,
|
name: qrName,
|
||||||
destinationUrl: destinationUrl,
|
destinationUrl: destinationUrl,
|
||||||
shortUrlId: qrType === 'dynamic' ? customSlug : undefined,
|
shortUrlId: qrType === 'dynamic' ? customSlug : undefined,
|
||||||
@@ -129,7 +129,7 @@ export default function EditQRPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => handleDownload('svg')}
|
onClick={() => handleDownload('svg')}
|
||||||
disabled={userRole === 'free' || userRole === 'free_trial'}
|
disabled={userRole === 'free' || userRole === 'free_trial'}
|
||||||
className={px-6 py-2.5 rounded-lg font-semibold shadow-sm transition-colors border border-gray-300 }
|
className={`px-6 py-2.5 rounded-lg font-semibold shadow-sm transition-colors border border-gray-300 ${userRole === 'free' || userRole === 'free_trial' ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white text-gray-700 hover:bg-gray-50'}`}
|
||||||
title={userRole === 'free' || userRole === 'free_trial' ? 'SVG export is for Pro users' : 'Download SVG'}
|
title={userRole === 'free' || userRole === 'free_trial' ? 'SVG export is for Pro users' : 'Download SVG'}
|
||||||
>
|
>
|
||||||
{userRole === 'free' || userRole === 'free_trial' ? 'Download SVG (Pro)' : 'Download SVG'}
|
{userRole === 'free' || userRole === 'free_trial' ? 'Download SVG (Pro)' : 'Download SVG'}
|
||||||
@@ -185,7 +185,7 @@ export default function EditQRPage() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={qrType === 'static'}
|
disabled={qrType === 'static'}
|
||||||
className={w-full rounded-lg shadow-sm p-3 border focus:ring-indigo-500 focus:border-indigo-500 text-gray-900 }
|
className={`w-full rounded-lg shadow-sm p-3 border focus:ring-indigo-500 focus:border-indigo-500 text-gray-900 ${qrType === 'static' ? 'bg-gray-100 border-gray-200 text-gray-500' : 'bg-white border-gray-300'}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -50,7 +50,7 @@ export default function DashboardPage() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
let url = '/qr';
|
let url = '/qr';
|
||||||
if (selectedFolder !== 'all') {
|
if (selectedFolder !== 'all') {
|
||||||
url += ?folderId= + selectedFolder;
|
url += `?folderId=${selectedFolder}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [res, authRes] = await Promise.all([
|
const [res, authRes] = await Promise.all([
|
||||||
@@ -93,7 +93,7 @@ export default function DashboardPage() {
|
|||||||
const handleDeleteFolder = async (id: string) => {
|
const handleDeleteFolder = async (id: string) => {
|
||||||
if (!confirm('Are you sure? QRs in this folder will be moved to the default folder.')) return;
|
if (!confirm('Are you sure? QRs in this folder will be moved to the default folder.')) return;
|
||||||
try {
|
try {
|
||||||
await api.delete(/folders/ + id);
|
await api.delete(`/folders/${id}`);
|
||||||
setFolders(folders.filter(f => f.id !== id));
|
setFolders(folders.filter(f => f.id !== id));
|
||||||
if (selectedFolder === id) setSelectedFolder('all');
|
if (selectedFolder === id) setSelectedFolder('all');
|
||||||
fetchQRs(); // refresh QRs just in case
|
fetchQRs(); // refresh QRs just in case
|
||||||
@@ -105,7 +105,7 @@ export default function DashboardPage() {
|
|||||||
const handleMoveQr = async (folderId: string | 'default') => {
|
const handleMoveQr = async (folderId: string | 'default') => {
|
||||||
if (!moveQrId) return;
|
if (!moveQrId) return;
|
||||||
try {
|
try {
|
||||||
await api.put(/qr/ + moveQrId, { folderId });
|
await api.put(`/qr/${moveQrId}`, { folderId });
|
||||||
setMoveQrId(null);
|
setMoveQrId(null);
|
||||||
fetchQRs();
|
fetchQRs();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -116,7 +116,7 @@ export default function DashboardPage() {
|
|||||||
const handleDelete = async (id: string) => {
|
const handleDelete = async (id: string) => {
|
||||||
if (!confirm('Are you sure you want to delete this QR code? The link will immediately stop working.')) return;
|
if (!confirm('Are you sure you want to delete this QR code? The link will immediately stop working.')) return;
|
||||||
try {
|
try {
|
||||||
await api.delete(/qr/ + id);
|
await api.delete(`/qr/${id}`);
|
||||||
setQrs(qrs.filter(qr => qr.id !== id));
|
setQrs(qrs.filter(qr => qr.id !== id));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('Failed to delete QR code');
|
alert('Failed to delete QR code');
|
||||||
@@ -127,7 +127,7 @@ export default function DashboardPage() {
|
|||||||
try {
|
try {
|
||||||
const QRCodeStyling = (await import('qr-code-styling')).default;
|
const QRCodeStyling = (await import('qr-code-styling')).default;
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://qr.houseofwebsites.ai';
|
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://qr.houseofwebsites.ai';
|
||||||
const shortUrl = baseUrl + '/l/' + qr.short_url_id;
|
const shortUrl = `${baseUrl}/l/${qr.short_url_id}`;
|
||||||
|
|
||||||
const qrCode = new QRCodeStyling({
|
const qrCode = new QRCodeStyling({
|
||||||
...qr.design_data,
|
...qr.design_data,
|
||||||
@@ -152,13 +152,13 @@ export default function DashboardPage() {
|
|||||||
<div className="space-y-1 mb-6">
|
<div className="space-y-1 mb-6">
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedFolder('all')}
|
onClick={() => setSelectedFolder('all')}
|
||||||
className={w-full text-left px-3 py-2 rounded-lg transition-colors text-sm font-medium }
|
className={`w-full text-left px-3 py-2 rounded-lg transition-colors text-sm font-medium ${selectedFolder === 'all' ? 'bg-indigo-50 text-indigo-700' : 'text-gray-700 hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
All QR Codes
|
All QR Codes
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedFolder('default')}
|
onClick={() => setSelectedFolder('default')}
|
||||||
className={w-full text-left px-3 py-2 rounded-lg transition-colors text-sm font-medium }
|
className={`w-full text-left px-3 py-2 rounded-lg transition-colors text-sm font-medium ${selectedFolder === 'default' ? 'bg-indigo-50 text-indigo-700' : 'text-gray-700 hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
Default Folder
|
Default Folder
|
||||||
</button>
|
</button>
|
||||||
@@ -167,11 +167,11 @@ export default function DashboardPage() {
|
|||||||
<div key={folder.id} className="flex group">
|
<div key={folder.id} className="flex group">
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedFolder(folder.id)}
|
onClick={() => setSelectedFolder(folder.id)}
|
||||||
className={lex-1 text-left px-3 py-2 rounded-l-lg transition-colors text-sm font-medium truncate }
|
className={`flex-1 text-left px-3 py-2 rounded-l-lg transition-colors text-sm font-medium truncate ${selectedFolder === folder.id ? 'bg-indigo-50 text-indigo-700' : 'text-gray-700 hover:bg-gray-100 group-hover:bg-gray-100'}`}
|
||||||
>
|
>
|
||||||
{folder.name}
|
{folder.name}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => handleDeleteFolder(folder.id)} className={px-2 rounded-r-lg text-gray-400 hover:text-red-600 transition-colors }>
|
<button onClick={() => handleDeleteFolder(folder.id)} className={`px-2 rounded-r-lg text-gray-400 hover:text-red-600 transition-colors ${selectedFolder === folder.id ? 'bg-indigo-50' : 'hover:bg-gray-100 group-hover:bg-gray-100'}`}>
|
||||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -225,20 +225,20 @@ export default function DashboardPage() {
|
|||||||
|
|
||||||
{/* QR Image Custom Thumbnail */}
|
{/* QR Image Custom Thumbnail */}
|
||||||
<div className="w-32 h-32 flex-shrink-0 flex items-center justify-center relative border border-gray-100 rounded-xl overflow-hidden bg-gray-50">
|
<div className="w-32 h-32 flex-shrink-0 flex items-center justify-center relative border border-gray-100 rounded-xl overflow-hidden bg-gray-50">
|
||||||
<LiveThumbnail options={{ ...qr.design_data, data: baseUrl + '/l/' + qr.short_url_id }} />
|
<LiveThumbnail options={{ ...qr.design_data, data: `${baseUrl}/l/${qr.short_url_id}` }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Info Area */}
|
{/* Info Area */}
|
||||||
<div className="flex-1 min-w-0 w-full">
|
<div className="flex-1 min-w-0 w-full">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<h3 className="text-xl font-bold text-gray-900 truncate">{qr.name}</h3>
|
<h3 className="text-xl font-bold text-gray-900 truncate">{qr.name}</h3>
|
||||||
<Link href={/dashboard/edit/ + qr.id} className="text-gray-400 hover:text-indigo-600 transition-colors" title="Edit QR settings">
|
<Link href={`/dashboard/edit/${qr.id}`} className="text-gray-400 hover:text-indigo-600 transition-colors" title="Edit QR settings">
|
||||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" /></svg>
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" /></svg>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<a href={${baseUrl}/l/} target="_blank" rel="noopener noreferrer" className="text-indigo-600 font-bold hover:underline flex items-center gap-1 text-sm bg-indigo-50 inline-flex px-3 py-1.5 rounded-lg border border-indigo-100 truncate max-w-full">
|
<a href={`${baseUrl}/l/${qr.short_url_id}`} target="_blank" rel="noopener noreferrer" className="text-indigo-600 font-bold hover:underline flex items-center gap-1 text-sm bg-indigo-50 inline-flex px-3 py-1.5 rounded-lg border border-indigo-100 truncate max-w-full">
|
||||||
{baseUrl}/l/{qr.short_url_id}
|
{baseUrl}/l/{qr.short_url_id}
|
||||||
<svg className="w-3 h-3 ml-1 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>
|
<svg className="w-3 h-3 ml-1 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>
|
||||||
</a>
|
</a>
|
||||||
@@ -273,7 +273,11 @@ export default function DashboardPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => handleDownload(qr, 'svg')}
|
onClick={() => handleDownload(qr, 'svg')}
|
||||||
disabled={userRole === 'free' || userRole === 'free_trial'}
|
disabled={userRole === 'free' || userRole === 'free_trial'}
|
||||||
className={lex-1 font-bold py-2 px-2 rounded-lg transition-colors flex justify-center items-center text-sm shadow-sm }
|
className={`flex-1 font-bold py-2 px-2 rounded-lg transition-colors flex justify-center items-center text-sm shadow-sm ${
|
||||||
|
userRole === 'free' || userRole === 'free_trial'
|
||||||
|
? 'bg-gray-200 text-gray-500 cursor-not-allowed'
|
||||||
|
: 'bg-green-500 hover:bg-green-600 text-white'
|
||||||
|
}`}
|
||||||
title={userRole === 'free' || userRole === 'free_trial' ? 'SVG export is for Pro users' : 'Download SVG'}
|
title={userRole === 'free' || userRole === 'free_trial' ? 'SVG export is for Pro users' : 'Download SVG'}
|
||||||
>
|
>
|
||||||
{userRole === 'free' || userRole === 'free_trial' ? 'SVG (Pro)' : 'SVG'}
|
{userRole === 'free' || userRole === 'free_trial' ? 'SVG (Pro)' : 'SVG'}
|
||||||
@@ -281,7 +285,7 @@ export default function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Link href={/dashboard/edit/ + qr.id} className="flex-1 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 font-semibold py-2 px-3 rounded-lg transition-colors text-sm text-center">
|
<Link href={`/dashboard/edit/${qr.id}`} className="flex-1 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 font-semibold py-2 px-3 rounded-lg transition-colors text-sm text-center">
|
||||||
Edit Link
|
Edit Link
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user