diff --git a/modern/frontend/src/App.jsx b/modern/frontend/src/App.jsx index 4c7e150..7a3bfc1 100644 --- a/modern/frontend/src/App.jsx +++ b/modern/frontend/src/App.jsx @@ -1,5 +1,6 @@ import React from 'react' import Integrations from './Integrations' +import Guilds from './Guilds' export default function App(){ return ( @@ -7,6 +8,7 @@ export default function App(){

LifeRPG Modern

Welcome — frontend scaffold. Connect to backend at /api/v1.

+ ) } diff --git a/modern/frontend/src/Guilds.jsx b/modern/frontend/src/Guilds.jsx new file mode 100644 index 0000000..c23f45e --- /dev/null +++ b/modern/frontend/src/Guilds.jsx @@ -0,0 +1,55 @@ +import React, {useState, useEffect} from 'react' + +const API = (path, opts) => fetch(path, {...(opts||{}), credentials: 'include'}).then(r=>r.json()) + +export default function Guilds(){ + const [guilds, setGuilds] = useState([]) + const [name, setName] = useState('') + const [members, setMembers] = useState([]) + const [selectedGuild, setSelectedGuild] = useState(null) + const [userId] = useState(1) + + useEffect(()=>{ API('/api/v1/guilds').then(setGuilds).catch(()=>setGuilds([])) }, []) + + function createGuild(){ + if(!name.trim()) return + API('/api/v1/guilds', {method:'POST', body: JSON.stringify({name, owner_id: userId}), headers: {'Content-Type':'application/json'}}) + .then(g=> setGuilds([...guilds, g])) + .catch(()=>{}) + } + + function loadMembers(gid){ + API(`/api/v1/guilds/${gid}/members`).then(setMembers).catch(()=>setMembers([])) + setSelectedGuild(gid) + } + + function addMember(gid){ + const uid = prompt('User ID to add:') + if(!uid) return + API(`/api/v1/guilds/${gid}/members`, {method:'POST', body: JSON.stringify({user_id: parseInt(uid)}), headers: {'Content-Type':'application/json'}}) + .then(()=> loadMembers(gid)) + .catch(()=> alert('failed')) + } + + return ( +
+

Guilds

+
+ setName(e.target.value)} placeholder="Guild name" /> + +
+
    + {guilds && guilds.length ? guilds.map(g=> ( +
  • + {g.name} (owner: {g.owner_id}) + +
  • + )):
  • No guilds
  • } +
+

Members

+
    + {members && members.length ? members.map(m=> (
  • User {m.user_id} — {m.role}
  • )) :
  • No members
  • } +
+
+ ) +} diff --git a/modern/frontend/src/Integrations.jsx b/modern/frontend/src/Integrations.jsx index 908fa29..b0d7457 100644 --- a/modern/frontend/src/Integrations.jsx +++ b/modern/frontend/src/Integrations.jsx @@ -6,6 +6,8 @@ export default function Integrations(){ const [integrations, setIntegrations] = useState([]) const [events, setEvents] = useState(null) const [userId] = useState(1) + const [msg, setMsg] = useState(null) + const [loadingId, setLoadingId] = useState(null) useEffect(()=>{ API(`/api/v1/users/${userId}/integrations`).then(d=>setIntegrations(d)).catch(()=>setIntegrations([])) @@ -17,7 +19,37 @@ export default function Integrations(){ } function fetchEvents(integrationId){ - API(`/api/v1/integrations/${integrationId}/google/events`).then(d=>setEvents(d)).catch(e=>setEvents({error: String(e)})) + setLoadingId(integrationId) + fetch(`/api/v1/integrations/${integrationId}/google/events`, {credentials:'include'}) + .then(r=>r.json()) + .then(d=>{ + setEvents(d) + setMsg('Fetched events') + }) + .catch(e=>setEvents({error: String(e)})) + .finally(()=>setLoadingId(null)) + } + + function removeIntegration(integrationId){ + if(!confirm('Remove integration?')) return + setLoadingId(integrationId) + fetch(`/api/v1/integrations/${integrationId}`, {method: 'DELETE', credentials: 'include'}) + .then(r=>r.json()) + .then(d=>{ + setMsg('Integration removed') + setIntegrations(integrations.filter(i=>i.id !== integrationId)) + }) + .catch(e=>setMsg('Failed to remove')) + .finally(()=>setLoadingId(null)) + } + + function syncIntegration(integrationId){ + setLoadingId(integrationId) + fetch(`/api/v1/integrations/${integrationId}/sync_to_habits`, {method:'POST', credentials:'include'}) + .then(r=>r.json()) + .then(d=>setMsg(`Synced ${d.count || 0} items`)) + .catch(e=>setMsg('Sync failed')) + .finally(()=>setLoadingId(null)) } return ( @@ -27,11 +59,17 @@ export default function Integrations(){

Your Integrations

+ {msg &&
{msg}
}

Events

{events? JSON.stringify(events, null, 2): 'No events fetched'}