// settings.jsx — หน้าเชื่อมต่อฐานข้อมูล (Supabase) + คู่มือติดตั้ง const SQL_TABLE = `-- 1) สร้างตารางเก็บงานซ่อม create table if not exists public.jobs ( id text primary key, phone text, customer_name text, status text, created_at timestamptz default now(), data jsonb not null ); create index if not exists jobs_phone_idx on public.jobs (phone); create index if not exists jobs_created_idx on public.jobs (created_at desc); -- 2) เปิด Row Level Security alter table public.jobs enable row level security; -- 3) นโยบายเริ่มต้น (เปิดให้ใช้งานได้ทันที) create policy "jobs_read" on public.jobs for select using (true); create policy "jobs_insert" on public.jobs for insert with check (true); create policy "jobs_update" on public.jobs for update using (true); create policy "jobs_delete" on public.jobs for delete using (true);`; const SQL_STORAGE = `-- ที่เก็บรูป (Storage bucket) insert into storage.buckets (id, name, public) values ('repair-photos', 'repair-photos', true) on conflict (id) do nothing; create policy "photos_read" on storage.objects for select using (bucket_id = 'repair-photos'); create policy "photos_write" on storage.objects for insert with check (bucket_id = 'repair-photos');`; // SQL ทางเลือก: รัดกุมสิทธิ์ให้เฉพาะพนักงานที่ล็อกอินแก้ไขได้ ส่วนลูกค้าอ่านสถานะได้ const SQL_SECURE = `-- เมื่อพร้อมใช้จริง: ให้เฉพาะพนักงานที่ล็อกอินเขียน/แก้ไข/ลบได้ -- (ลูกค้ายังเช็คสถานะได้ เพราะ select ยังเปิดอ่าน) drop policy if exists "jobs_insert" on public.jobs; drop policy if exists "jobs_update" on public.jobs; drop policy if exists "jobs_delete" on public.jobs; create policy "jobs_insert_auth" on public.jobs for insert to authenticated with check (true); create policy "jobs_update_auth" on public.jobs for update to authenticated using (true); create policy "jobs_delete_auth" on public.jobs for delete to authenticated using (true); -- รูป: ให้เฉพาะพนักงานที่ล็อกอินอัปโหลดได้ drop policy if exists "photos_write" on storage.objects; create policy "photos_write_auth" on storage.objects for insert to authenticated with check (bucket_id = 'repair-photos');`; function CopyBtn({ text, label = 'คัดลอก' }) { const [done, setDone] = React.useState(false); return ( ); } function CodeBlock({ code }) { return (
{code}
); } function StaffAccounts() { const online = isOnline(); const user = Auth.user(); // online: สมัครบัญชีพนักงานใหม่ const [email, setEmail] = React.useState(''); const [pass, setPass] = React.useState(''); const [busy, setBusy] = React.useState(false); const [msg, setMsg] = React.useState(null); // local: ตั้งรหัสผ่านในเครื่อง const cred = getLocalCred(); const [lu, setLu] = React.useState(cred.user); const [lp, setLp] = React.useState(cred.pass); const doSignup = async () => { setBusy(true); setMsg(null); try { await Auth.signUp(email, pass); setMsg({ ok: true, t: 'สร้างบัญชีแล้ว — หากเปิด Email confirmation ใน Supabase พนักงานต้องยืนยันอีเมลก่อนล็อกอิน' }); setEmail(''); setPass(''); } catch (e) { setMsg({ ok: false, t: e.message }); } setBusy(false); }; const saveLocal = () => { if (!lu.trim() || !lp) { setMsg({ ok: false, t: 'กรอกชื่อผู้ใช้และรหัสผ่าน' }); return; } setLocalCred({ user: lu.trim(), pass: lp, name: 'พนักงาน' }); setMsg({ ok: true, t: 'บันทึกรหัสผ่านโหมดทดลองแล้ว' }); }; return (
บัญชีพนักงาน
{user &&
ล็อกอินอยู่:  {user.name}
} {msg &&
{msg.t}
} {online ? ( <>

เพิ่มพนักงานได้ที่ Supabase → Authentication → Users → Add user (กำหนดอีเมล/รหัสผ่าน) หรือสร้างจากตรงนี้ (ต้องเปิด Email signups ใน Supabase):

setEmail(e.target.value)} placeholder="อีเมลพนักงานใหม่" /> setPass(e.target.value)} placeholder="รหัสผ่าน (อย่างน้อย 6 ตัว)" />
) : ( <>

โหมดทดลองใช้รหัสผ่านในเครื่องนี้ (ตั้งค่าได้) — เมื่อเชื่อมต่อ Supabase แล้วจะใช้บัญชีอีเมลจริงแทน

setLu(e.target.value)} placeholder="ชื่อผู้ใช้" /> setLp(e.target.value)} placeholder="รหัสผ่าน" />
)}
); } function Settings({ onChanged }) { const cfg = getSupaConfig(); const [url, setUrl] = React.useState(cfg ? cfg.url : ''); const [key, setKey] = React.useState(cfg ? cfg.key : ''); const [siteUrl, setSiteUrlVal] = React.useState(getSiteUrl()); const [testing, setTesting] = React.useState(false); const [result, setResult] = React.useState(null); const [pushing, setPushing] = React.useState(false); const [guideOpen, setGuideOpen] = React.useState(!isOnline()); const online = isOnline(); const doTest = async () => { setTesting(true); setResult(null); try { const r = await DB.testConnection({ url, key }); setResult(r.ok ? { ok: true, msg: 'เชื่อมต่อสำเร็จ! พบตาราง jobs พร้อมใช้งาน' } : { ok: false, msg: 'เชื่อมต่อไม่ได้ (' + r.status + ') — ตรวจ URL/Key และว่าได้สร้างตาราง jobs แล้วหรือยัง' }); } catch (e) { setResult({ ok: false, msg: 'เชื่อมต่อไม่ได้: ' + e.message + ' (ตรวจ URL ให้ถูกต้อง)' }); } setTesting(false); }; const doSave = () => { setSupaConfig({ url, key }); setResult({ ok: true, msg: 'บันทึกแล้ว — ระบบกำลังใช้ฐานข้อมูลออนไลน์' }); onChanged && onChanged(); }; const doDisconnect = () => { setSupaConfig(null); setUrl(''); setKey(''); setResult(null); onChanged && onChanged(); }; const doPush = async () => { setPushing(true); try { const n = await DB.pushLocalSeed(); setResult({ ok: true, msg: 'ส่งข้อมูลตัวอย่างขึ้นฐานข้อมูลแล้ว ' + n + ' รายการ' }); onChanged && onChanged(); } catch (e) { setResult({ ok: false, msg: 'ส่งข้อมูลไม่สำเร็จ: ' + e.message }); } setPushing(false); }; return (

เชื่อมต่อฐานข้อมูล

เก็บข้อมูลออนไลน์ ใช้ร่วมกันได้หลายเครื่อง · Online database

{online ? 'ออนไลน์ (Supabase)' : 'โหมดทดลอง (เครื่องนี้)'}
{online ? <>ขณะนี้ข้อมูลถูกบันทึกบนฐานข้อมูลออนไลน์ — ทุกเครื่องที่เปิดเว็บนี้และใส่คีย์เดียวกันจะเห็นข้อมูลตรงกัน และลูกค้าเช็คสถานะผ่าน QR ได้จากทุกที่ : <>ขณะนี้เป็น “โหมดทดลอง” — ข้อมูลเก็บในเบราว์เซอร์เครื่องนี้เท่านั้น (ยังไม่แชร์ข้ามเครื่อง) ทำตามขั้นตอนด้านล่างเพื่อเปิดใช้ฐานข้อมูลออนไลน์}
คีย์เชื่อมต่อ Supabase