173 lines
6.2 KiB
TypeScript
173 lines
6.2 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import QRCode from 'qrcode';
|
|
import { supabase } from '~/lib/supabase';
|
|
|
|
export default function Dashboard() {
|
|
const [name, setName] = useState('');
|
|
const [glbUrl, setGlbUrl] = useState('');
|
|
const [usdzUrl, setUsdzUrl] = useState('');
|
|
const [assets, setAssets] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const fetchAssets = async () => {
|
|
const { data } = await supabase
|
|
.from('ar_assets')
|
|
.select('*')
|
|
.order('created_at', { ascending: false });
|
|
|
|
setAssets(data || []);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchAssets();
|
|
}, []);
|
|
|
|
const createAsset = async () => {
|
|
if (!name || !glbUrl || !usdzUrl) {
|
|
alert('Fill all fields');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
const { data, error } = await supabase
|
|
.from('ar_assets')
|
|
.insert({
|
|
name,
|
|
glb_url: glbUrl,
|
|
usdz_url: usdzUrl,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (error || !data) {
|
|
alert(error?.message);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
// const qrUrl = `http://10.20.2.107:5173/ar/${data.id}`;
|
|
const qrUrl = `${window.location.origin}/ar/${data.id}`;
|
|
|
|
const qrImage = await QRCode.toDataURL(qrUrl, {
|
|
width: 512,
|
|
margin: 2,
|
|
});
|
|
|
|
const qrBlob = await (await fetch(qrImage)).blob();
|
|
const filePath = `${data.id}.png`;
|
|
|
|
await supabase.storage.from('qr-codes').upload(filePath, qrBlob, {
|
|
upsert: true,
|
|
contentType: 'image/png',
|
|
});
|
|
|
|
const { data: publicUrl } = supabase.storage
|
|
.from('qr-codes')
|
|
.getPublicUrl(filePath);
|
|
|
|
await supabase
|
|
.from('ar_assets')
|
|
.update({
|
|
qr_url: qrUrl,
|
|
qr_image_url: publicUrl.publicUrl,
|
|
})
|
|
.eq('id', data.id);
|
|
|
|
setName('');
|
|
setGlbUrl('');
|
|
setUsdzUrl('');
|
|
setLoading(false);
|
|
fetchAssets();
|
|
};
|
|
|
|
return (
|
|
<div className='min-h-screen bg-gray-50 px-6 py-10'>
|
|
<div className='mx-auto max-w-5xl space-y-10'>
|
|
<div>
|
|
<h1 className='text-3xl font-bold text-gray-900'>
|
|
AR Asset Dashboard
|
|
</h1>
|
|
<p className='mt-1 text-gray-500'>
|
|
Manage 3D assets and generate AR QR codes
|
|
</p>
|
|
</div>
|
|
|
|
<div className='rounded-2xl bg-white p-6 shadow-sm'>
|
|
<h2 className='mb-4 text-lg font-semibold text-gray-800'>
|
|
Create New Asset
|
|
</h2>
|
|
|
|
<div className='grid gap-4 md:grid-cols-3 text-black'>
|
|
<input
|
|
className='rounded-lg border border-gray-300 px-4 py-2 text-sm focus:border-black focus:outline-none'
|
|
placeholder='Asset name'
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
/>
|
|
<input
|
|
className='rounded-lg border border-gray-300 px-4 py-2 text-sm focus:border-black focus:outline-none'
|
|
placeholder='GLB URL'
|
|
value={glbUrl}
|
|
onChange={(e) => setGlbUrl(e.target.value)}
|
|
/>
|
|
<input
|
|
className='rounded-lg border border-gray-300 px-4 py-2 text-sm focus:border-black focus:outline-none'
|
|
placeholder='USDZ URL'
|
|
value={usdzUrl}
|
|
onChange={(e) => setUsdzUrl(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
onClick={createAsset}
|
|
disabled={loading}
|
|
className='mt-6 inline-flex items-center justify-center rounded-xl bg-black px-6 py-2.5 text-sm font-medium text-white transition hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-60'
|
|
>
|
|
{loading ? 'Creating…' : 'Create Asset'}
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
<h2 className='mb-4 text-lg font-semibold text-gray-800'>
|
|
Generated Assets
|
|
</h2>
|
|
|
|
{assets.length === 0 ? (
|
|
<p className='text-sm text-gray-500'>
|
|
No assets created yet.
|
|
</p>
|
|
) : (
|
|
<div className='grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3'>
|
|
{assets.map((asset) => (
|
|
<div
|
|
key={asset.id}
|
|
className='rounded-2xl bg-white p-5 shadow-sm transition hover:shadow-md'
|
|
>
|
|
<h3 className='mb-3 text-base font-semibold text-gray-900'>
|
|
{asset?.name}
|
|
</h3>
|
|
|
|
{asset?.qr_image_url ? (
|
|
<img
|
|
src={asset?.qr_image_url}
|
|
alt={asset?.name}
|
|
className='mx-auto w-40 rounded-lg'
|
|
/>
|
|
) : (
|
|
<div className='flex h-40 items-center justify-center rounded-lg bg-gray-100 text-sm text-gray-400'>
|
|
No QR
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|